]>
Commit | Line | Data |
---|---|---|
45030404 MKG |
1 | =head1 Summary of initialdata files |
2 | ||
3 | It's often useful to be able to test configuration/database changes and then | |
4 | apply the same changes in production without manually clicking around. It's | |
5 | also helpful if you're developing customizations or extensions to be able to | |
6 | get a fresh database back to the state you want for testing/development. | |
7 | ||
8 | This documentation applies to careful and thorough sysadmins as well as | |
9 | extension authors who need to make database changes easily and repeatably for | |
10 | new installs or upgrades. | |
11 | ||
12 | =head1 Examples | |
13 | ||
14 | RT ships with many initialdata files, only one of which is used to | |
15 | configure a fresh install; the rest are used for upgrades, but function | |
16 | the same despite being named differently. | |
17 | ||
18 | etc/initialdata | |
19 | etc/upgrade/*/content | |
20 | ||
21 | The upgrade "content" files are meant to be incremental changes applied on top | |
22 | of one another while the top level initialdata file is for fresh RT installs. | |
23 | ||
24 | Extensions may also ship with database changes in such files. You may find | |
25 | some in your install with: | |
26 | ||
27 | find local/plugins -name initialdata -or -name content | |
28 | ||
29 | =head1 What can be in an initialdata file? | |
30 | ||
31 | initialdata files are Perl, but often consist primarily of a bunch of data | |
32 | structures defining the new records you want and not much extra code. There's | |
33 | nothing stopping you from writing a bunch of code, however! | |
34 | ||
35 | The basic template of a new initialdata file should look something like this: | |
36 | ||
37 | use strict; | |
38 | use warnings; | |
39 | ||
40 | our @Queues = ( | |
41 | # some definitions here | |
42 | ); | |
43 | ||
44 | our @Groups = ( | |
45 | # some other definitions here | |
46 | ); | |
47 | ||
48 | 1; | |
49 | ||
50 | The C<@Queues> and C<@Groups> arrays are expected by RT and should contain | |
51 | hashref definitions. There are many other arrays RT will look for and act on, | |
52 | described below. None are required, all may be used. Keep in mind that since | |
53 | they're just normal Perl arrays, you can C<push> onto them from a loop or | |
54 | C<grep> out definitions based on conditionals or generate their content with | |
55 | C<map>, etc. | |
56 | ||
57 | The complete list of possible arrays which can be used, along with | |
58 | descriptions of the values to place in them, is below. | |
59 | ||
60 | =head2 C<@Users> | |
61 | ||
62 | push @Users, { | |
63 | Name => 'john.doe', | |
64 | Password => 'changethis', | |
65 | Language => 'fr', | |
66 | Timezone => 'America/Vancouver', | |
67 | Privileged => 1, | |
68 | Disabled => 0, | |
69 | }; | |
70 | ||
71 | Each hashref in C<@Users> is treated as a new user to create and passed | |
72 | straight into C<< RT::User->Create >>. All of the normal user fields are | |
73 | available, as well as C<Privileged> and C<Disabled> (both booleans) which will | |
74 | do the appropriate internal group/flag handling. | |
75 | ||
76 | For a full list of fields, read the documentation for L<RT::User/Create>. | |
77 | ||
78 | =head2 C<@Groups> | |
79 | ||
80 | push @Groups, { | |
45030404 MKG |
81 | Name => 'Example Employees', |
82 | Description => 'All of the employees of my company', | |
c33a4027 MKG |
83 | Members => { Users => [ qw/ alexmv trs falcone / ], |
84 | Groups => [ qw/ extras / ] }, | |
45030404 MKG |
85 | }; |
86 | ||
87 | Creates a new L<RT::Group> for each hashref. In almost all cases you'll want | |
88 | to follow the example above to create a group just as if you had done it from | |
af59614d | 89 | the admin interface. |
45030404 | 90 | |
c33a4027 MKG |
91 | In addition to the C<Members> option shown above, which can take both |
92 | users and groups, the C<MemberOf> field may be a single value or an | |
93 | array ref. Each value should be a user-defined group name or hashref to | |
94 | pass into L<RT::Group/LoadByCols>. Each group found will have the new | |
95 | group added as a member. | |
45030404 MKG |
96 | |
97 | =head2 C<@Queues> | |
98 | ||
99 | push @Queues, { | |
100 | Name => 'Helpdesk', | |
101 | CorrespondAddress => 'help@example.com', | |
102 | CommentAddress => 'help-comment@example.com', | |
103 | }; | |
104 | ||
105 | Creates a new L<RT::Queue> for each hashref. Refer to the documentation of | |
106 | L<RT::Queue/Create> for the fields you can use. | |
107 | ||
108 | =head2 C<@CustomFields> | |
109 | ||
110 | push @CustomFields, { | |
45030404 MKG |
111 | Name => 'Favorite color', |
112 | Type => 'FreeformSingle', | |
113 | LookupType => 'RT::Queue-RT::Ticket', | |
114 | }; | |
115 | ||
116 | Creates a new L<RT::CustomField> for each hashref. It is the most complex of | |
117 | the initialdata structures. The most commonly used fields are: | |
118 | ||
119 | =over 4 | |
120 | ||
121 | =item C<Name> | |
122 | ||
123 | The name of this CF as displayed in RT. | |
124 | ||
125 | =item C<Description> | |
126 | ||
127 | A short summary of what this CF is for. | |
128 | ||
af59614d | 129 | =item C<ApplyTo> |
45030404 | 130 | |
af59614d MKG |
131 | May be a single value, or an array reference of such; each should be |
132 | either an ID or Name. If omitted, the CF is applied globally. This | |
133 | should not be used for User or Group custom fields. | |
134 | ||
135 | This argument may also be passed via C<Queue>, for backwards | |
136 | compatibility, which also defaults the C<LookupType> to | |
137 | C<RT::Queue-RT::Ticket>. | |
45030404 MKG |
138 | |
139 | =item C<Type> | |
140 | ||
141 | One of the following on the left hand side: | |
142 | ||
143 | SelectSingle # Select one value | |
144 | SelectMultiple # Select multiple values | |
145 | ||
146 | FreeformSingle # Enter one value | |
147 | FreeformMultiple # Enter multiple values | |
148 | ||
149 | Text # Fill in one text area | |
150 | Wikitext # Fill in one wikitext area | |
151 | ||
152 | BinarySingle # Upload one file | |
153 | BinaryMultiple # Upload multiple files | |
154 | ||
155 | ImageSingle # Upload one image | |
156 | ImageMultiple # Upload multiple images | |
157 | ||
158 | Combobox # Combobox: Select or enter one value | |
159 | ||
160 | AutocompleteSingle # Enter one value with autocompletion | |
161 | AutocompleteMultiple # Enter multiple values with autocompletion | |
162 | ||
163 | Date # Select date | |
164 | DateTime # Select datetime | |
165 | ||
166 | IPAddressSingle # Enter one IP address | |
167 | IPAddressMultiple # Enter multiple IP addresses | |
168 | ||
169 | IPAddressRangeSingle # Enter one IP address range | |
170 | IPAddressRangeMultiple # Enter multiple IP address ranges | |
171 | ||
172 | If you don't specify "Single" or "Multiple" in the type, you must specify | |
173 | C<MaxValues>. | |
174 | ||
175 | =item C<LookupType> | |
176 | ||
177 | Labeled in the CF admin page as "Applies to". This determines whether your CF | |
178 | is for Tickets, Transactions, Users, Groups, or Queues. Possible values: | |
179 | ||
180 | RT::Queue-RT::Ticket # Tickets | |
181 | RT::Queue-RT::Ticket-RT::Transaction # Transactions | |
182 | RT::User # Users | |
183 | RT::Group # Groups | |
184 | RT::Queue # Queues | |
af59614d | 185 | RT::Class-RT::Article # Articles |
45030404 MKG |
186 | |
187 | Ticket CFs are the most common, meaning C<RT::Queue-RT::Ticket> is the most | |
188 | common C<LookupType>. | |
189 | ||
190 | =item C<RenderType> | |
191 | ||
192 | Only valid when C<Type> is "Select". Controls how the CF is displayed when | |
193 | editing it. Valid values are: C<Select box>, C<List>, and C<Dropdown>. | |
194 | ||
195 | C<List> is either a list of radio buttons or a list of checkboxes depending on | |
196 | C<MaxValues>. | |
197 | ||
198 | =item C<MaxValues> | |
199 | ||
200 | Determines whether this CF is a Single or Multiple type. 0 means multiple. 1 | |
201 | means single. | |
202 | ||
203 | Make sure to set the C<MaxValues> field appropriately, otherwise you can end up | |
204 | with unsupported CF types like a "Select multiple dates" (it doesn't Just | |
205 | Work). | |
206 | ||
207 | You can also use old-style C<Type>s which end with "Single" or "Multiple", for | |
208 | example: SelectSingle, SelectMultiple, FreeformSingle, etc. | |
209 | ||
210 | =item C<Values> | |
211 | ||
212 | C<Values> should be an array ref (never a single value!) of hashrefs | |
213 | representing new L<RT::CustomFieldValue> objects to create for the new custom | |
214 | field. This only makes sense for "Select" CFs. An example: | |
215 | ||
216 | my $i = 1; | |
217 | push @CustomFields, { | |
45030404 MKG |
218 | LookupType => 'RT::Queue-RT::Ticket', # for Tickets |
219 | Name => 'Type of food', | |
220 | Type => 'SelectSingle', # SelectSingle is the same as: Type => 'Select', MaxValues => 1 | |
221 | RenderType => 'Dropdown', | |
222 | Values => [ | |
223 | { Name => 'Fruit', Description => 'Berries, peaches, tomatos, etc', SortOrder => $i++ }, | |
224 | { Name => 'Vegetable', Description => 'Asparagus, peas, lettuce, etc', SortOrder => $i++ }, | |
225 | # more values as such... | |
226 | ], | |
227 | }; | |
228 | ||
229 | In order to ensure the same sorting of C<Values>, set C<SortOrder> inside each | |
230 | value. A clever way to do this easily is with a simple variable you increment | |
231 | each time (as above with C<$i>). You can use the same variable throughout the | |
232 | whole file, and don't need one per CF. | |
233 | ||
234 | =item C<BasedOn> | |
235 | ||
236 | Name or ID of another Select Custom Field. This makes the named CF the source | |
237 | of categories for your values. | |
238 | ||
239 | =item C<Pattern> | |
240 | ||
241 | The regular expression text (not C<qr//>!) used to validate values. | |
242 | ||
243 | =back | |
244 | ||
245 | Refer to the documentation and implementation of L<RT::CustomField/Create> and | |
246 | L<RT::CustomFieldValue/Create> for the full list of available fields and | |
247 | allowed values. | |
248 | ||
249 | =head2 C<@ACL> | |
250 | ||
251 | C<@ACL> is very useful for granting rights on your newly created records or | |
252 | setting up a standard system configuration. It is one of the most complex | |
253 | initialdata structures. | |
254 | ||
255 | =head3 Pick a Right | |
256 | ||
257 | All ACL definitions expect a key named C<Right> with the internal right name | |
258 | you want to grant. The internal right names are visible in RT's admin | |
259 | interface in grey next to the longer descriptions. | |
260 | ||
261 | =head3 Pick a level: on a queue, on a CF, or globally | |
262 | ||
263 | After picking a C<Right>, you need to specify on what object the right is | |
264 | granted. This is B<different> than the user/group/role receiving the right. | |
265 | ||
266 | =over 4 | |
267 | ||
268 | =item Granted on a custom field by name (or ID), potentially a global or queue | |
269 | ||
270 | CF => 'Name', | |
af59614d | 271 | LookupType => 'RT::User', # optional, in case you need to disambiguate |
45030404 MKG |
272 | |
273 | =item Granted on a queue | |
274 | ||
275 | Queue => 'Name', | |
276 | ||
277 | =item Granted on a custom field applied to a specific queue | |
278 | ||
279 | CF => 'Name', | |
280 | Queue => 'Name', | |
281 | ||
c33a4027 MKG |
282 | =item Granted on a custom field applied to some other object |
283 | ||
284 | # This finds the CF named "Name" applied to Articles in the | |
285 | # "Responses" class | |
286 | CF => 'Name', | |
287 | LookupType => RT::Article->CustomFieldLookupType, | |
288 | ObjectId => 'Responses', | |
289 | ||
af59614d MKG |
290 | =item Granted on some other object (article Classes, etc) |
291 | ||
292 | ObjectType => 'RT::Class', | |
293 | ObjectId => 'Name', | |
294 | ||
45030404 MKG |
295 | =item Granted globally |
296 | ||
297 | Specifying none of the above will get you a global right. | |
298 | ||
299 | =back | |
300 | ||
301 | There is currently no way to grant rights on a group or article class level. | |
302 | Note that you can grant rights B<to> a group; see below. If you need to grants | |
303 | rights on a group or article class level, you'll need to write an C<@Final> | |
304 | subref to handle it using the RT Perl API. | |
305 | ||
306 | =head3 Pick a Principal: User or Group or Role | |
307 | ||
308 | Finally you need to specify to what system group, system/queue role, | |
309 | user defined group, or user you want to grant the right B<to>. | |
310 | ||
311 | =over 4 | |
312 | ||
313 | =item An internal user group | |
314 | ||
315 | GroupDomain => 'SystemInternal', | |
316 | GroupType => 'Everyone, Privileged, or Unprivileged' | |
317 | ||
318 | =item A system-level role | |
319 | ||
320 | GroupDomain => 'RT::System-Role', | |
321 | GroupType => 'Requestor, Owner, AdminCc, or Cc' | |
322 | ||
323 | =item A queue-level role | |
324 | ||
325 | GroupDomain => 'RT::Queue-Role', | |
326 | Queue => 'Name', | |
327 | GroupType => 'Requestor, Owner, AdminCc, or Cc', | |
328 | ||
329 | =item A group you created | |
330 | ||
331 | GroupDomain => 'UserDefined', | |
332 | GroupId => 'Name' | |
333 | ||
334 | =item Individual user | |
335 | ||
336 | UserId => 'Name or email or ID' | |
337 | ||
338 | =back | |
339 | ||
340 | =head3 Common cases | |
341 | ||
342 | You're probably looking for definitions like these most of the time. | |
343 | ||
344 | =over 4 | |
345 | ||
346 | =item Grant a global right to a group you created | |
347 | ||
348 | { Right => '...', | |
349 | GroupDomain => 'UserDefined', | |
350 | GroupId => 'Name' } | |
351 | ||
352 | =item Grant a queue-level right to a group you created | |
353 | ||
354 | { Queue => 'Name', | |
355 | Right => '...', | |
356 | GroupDomain => 'UserDefined', | |
357 | GroupId => 'Name' } | |
358 | ||
359 | =item Grant a CF-level right to a group you created | |
360 | ||
361 | { CF => 'Name', | |
362 | Right => '...', | |
363 | GroupDomain => 'UserDefined', | |
364 | GroupId => 'Name' } | |
365 | ||
366 | =back | |
367 | ||
368 | Since you often want to grant a list of rights on the same object/level to the | |
369 | same role/group/user, we generally use Perl loops and operators to aid in the | |
370 | generation of C<@ACL> without repeating ourselves. | |
371 | ||
372 | # Give Requestors globally the right to see tickets, reply, and see the | |
373 | # queue their ticket is in | |
374 | push @ACL, map { | |
375 | { | |
376 | Right => $_, | |
377 | GroupDomain => 'RT::System-Role', | |
378 | GroupType => 'Requestor', | |
379 | } | |
380 | } qw(ShowTicket ReplyToTicket SeeQueue); | |
381 | ||
382 | =head3 Troubleshooting | |
383 | ||
384 | The best troubleshooting is often to see how the rights you define in C<@ACL> | |
385 | show up in the RT admin interface. | |
386 | ||
387 | =head2 C<@Scrips> | |
388 | ||
389 | Creates a new L<RT::Scrip> for each hashref. Refer to the documentation of | |
390 | L<RT::Scrip/Create> for the fields you can use. | |
391 | ||
392 | Additionally, the C<Queue> field is specially handled to make it easier to | |
393 | setup the same Scrip on multiple queues: | |
394 | ||
395 | =over 4 | |
396 | ||
397 | =item Globally | |
398 | ||
399 | Queue => 0, | |
400 | ||
401 | =item Single queue | |
402 | ||
403 | Queue => 'General', # Name or ID | |
404 | ||
405 | =item Multiple queues | |
406 | ||
407 | Queue => ['General', 'Helpdesk', 13], # Array ref of Name or ID | |
408 | ||
409 | =back | |
410 | ||
411 | =head2 C<@ScripActions> | |
412 | ||
413 | Creates a new L<RT::ScripAction> for each hashref. Refer to the documentation | |
414 | of L<RT::ScripAction/Create> for the fields you can use. | |
415 | ||
416 | =head2 C<@ScripConditions> | |
417 | ||
418 | Creates a new L<RT::ScripCondition> for each hashref. Refer to the | |
419 | documentation of L<RT::ScripCondition/Create> for the fields you can use. | |
420 | ||
421 | =head2 C<@Templates> | |
422 | ||
423 | Creates a new L<RT::Template> for each hashref. Refer to the documentation of | |
424 | L<RT::Template/Create> for the fields you can use. | |
425 | ||
426 | =head2 C<@Attributes> | |
427 | ||
428 | An array of L<RT::Attribute>s to create. You likely don't need to mess with | |
429 | this. If you do, know that the key C<Object> is expected to be an | |
c33a4027 MKG |
430 | L<RT::Record> object or a subroutine reference that returns an object on which |
431 | to call C<AddAttribute>. If you don't provide C<Object> or it's undefined, | |
432 | C<< RT->System >> will be used. | |
433 | ||
434 | Here is an example of using a subroutine reference as a value for Object: | |
435 | ||
436 | @Attributes = ({ | |
437 | Name => 'SavedSearch', | |
438 | Description => 'New Tickets in SomeQueue', | |
439 | Object => sub { | |
440 | my $GroupName = 'SomeQueue Group'; | |
441 | my $group = RT::Group->new( RT->SystemUser ); | |
442 | ||
443 | my( $ret, $msg ) = $group->LoadUserDefinedGroup( $GroupName ); | |
444 | die $msg unless $ret; | |
445 | ||
446 | return $group; | |
447 | }, | |
448 | Content => { | |
449 | Format => <<' END_OF_FORMAT', | |
450 | .... | |
451 | END_OF_FORMAT | |
452 | Query => "Status = 'new' AND Queue = 'SomeQueue'", | |
453 | OrderBy => 'id', | |
454 | Order => 'DESC' | |
455 | }, | |
456 | }); | |
45030404 MKG |
457 | |
458 | =head2 C<@Initial> | |
459 | ||
460 | =head2 C<@Final> | |
461 | ||
462 | C<@Initial> and C<@Final> are special and let you write your own processing | |
463 | code that runs before anything else or after everything else. They are | |
464 | expected to be arrays of subrefs (usually anonymous) like so: | |
465 | ||
466 | our @Final = (sub { | |
467 | RT->Logger->info("Finishing up!"); | |
468 | }); | |
469 | ||
470 | You have the full power of RT's Perl libraries at your disposal. Be sure to do | |
471 | error checking and log any errors with C<< RT->Logger->error("...") >>! | |
472 | ||
473 | =head1 What's missing? | |
474 | ||
475 | There is currently no way, short of writing code in C<@Final> or C<@Initial>, | |
476 | to easily create B<Classes>, B<Topics>, or B<Articles> from initialdata files. | |
477 | ||
478 | =head1 Running an initialdata file | |
479 | ||
480 | sbin/rt-setup-database --action insert --datafile /path/to/your/initialdata | |
481 | ||
482 | This may prompt you for a database password. | |
483 | ||
484 | =head1 Implementation details | |
485 | ||
486 | All the handling of initialdata files is done in C<< RT::Handle->InsertData >>. | |
487 | If you want to know B<exactly> what's happening with each array, your best bet | |
488 | is to start reading the code there. | |
489 | ||
490 | RT takes care of the ordering so that your new queues are created before it | |
491 | processes the new ACLs for those queues. This lets you refer to new queues you | |
492 | just created by Name. |