Upgrade to 4.2.8
[usit-rt.git] / docs / writing_extensions.pod
1 =head1 Introduction
2
3 RT has a lot of core features, but sometimes you have a problem to solve
4 that's beyond the scope of just configuration. The standard way to add
5 features to RT is with an extension. You can see the large number of
6 freely available extensions on CPAN under the RT::Extension namespace
7 to get an idea what's already out there. We also list some of the more
8 useful extensions on the Best Practical website at
9 L<http://www.bestpractical.com/rt/extensions.html>
10
11 After looking through those, you still may not find what you need, so
12 you'll want to write your own extension. Through the years there have
13 been different ways to safely and effectively add things onto RT.
14 This document describes the current best practice which should allow
15 you to add what you need and still be able to safely upgrade RT
16 in the future.
17
18 =head1 Getting Started
19
20 There are a few modules that will set up your initial sandbox for you
21 to get you started. Install these modules from CPAN:
22
23 =over
24
25 =item Module::Install::RTx
26
27 Sets up your extension to be installed using Module::Install.
28
29 =item Dist::Zilla::MintingProfile::RTx
30
31 Provides some tools for managing your distribution. Handy even if you're
32 not putting your code on CPAN.
33
34 =back
35
36 If this is your first time using L<Dist::Zilla>, you can set up your
37 CPAN details by running:
38
39     dzil setup
40
41 You can read about L<Dist::Zilla> and the C<dzil> command at L<http://dzil.org>.
42
43 Change to the directory that will be the parent directory for your new
44 extension and run the following, replacing Demo with a descriptive name
45 for your new extension:
46
47     dzil new -P RTx RT-Extension-Demo
48
49 You'll see something like:
50
51     [DZ] making target dir /some-dir/RT-Extension-Demo
52     [DZ] writing files to /some-dir/RT-Extension-Demo
53     [DZ] dist minted in ./RT-Extension-Demo
54
55 If you're stuck on a name, take a look at some of the existing RT extensions.
56 You can also ask around IRC (#rt on irc.perl.org) to see what people think
57 makes sense for what the extension will do.
58
59 You'll now have a directory with the basic files for your extension.
60 Included is a F<gitignore> file, which is handy if you use git for your version
61 control like we do. If you don't use git, feel free to delete it, but we hope
62 you're using some sort of version control for your work.
63
64 =head1 Extension Directories
65
66 There are several places to put code to provide your new features
67 and if you follow the guidelines below, you'll make sure things
68 get installed in the right places when you're ready to use it. These standards
69 apply to RT 4.0 and 4.2 and any differences between the two are noted below.
70
71 =head2 Module Code
72
73 In your new extension directory you'll already have a
74 C<lib/RT/Extension/Demo.pm> file, which is just a standard perl module.
75 As you start writing code, you can use all of the standard RT libraries
76 because your extension will be running in the context of RT and those
77 are already pulled in. You can also create more modules under C<lib>
78 as needed.
79
80 =head2 Mason Code
81
82 RT provides callbacks throughout its Mason templates to give you hooks to
83 add features. The easiest way to modify RT is to add Mason template files
84 that will use these callbacks. See L</Callbacks> for more information.
85 Your Mason templates should go in an C<html> directory with the appropriate
86 directory structure to make sure the callbacks are executed.
87
88 If you are creating completely new pages for RT, you can put these under the
89 C<html> directory also. You can create subdirectories as needed to add the
90 page to existing RT paths (like Tools) or to create new directories for your
91 extension.
92
93 =head2 CSS and Javascript
94
95 Where these files live differs between RT 4.2 and above, and RT 4.0 and
96 below; if you need your extension to be compatible with both, you may
97 need to provide both configurations.  On RT 4.2 and above, create a
98 C<static> directory at the top level under your extension, and under
99 that a C<css> directory and a C<js> directory. Before RT 4.2, you should
100 create C<css> and C<js> directories in C<html/NoAuth/>.
101
102 To add files to RT's include paths, you can use the L<RT/AddStyleSheets> and
103 L<RT/AddJavascript> methods available in the L<RT> module. You can put the
104 lines near the top of your module code (in your "Demo.pm" file). If you set up
105 the paths correctly, you should only need to set the file names like this:
106
107     RT->AddStyleSheets('myextension.css');
108     RT->AddJavaScript('myextension.js');
109
110 =head2 Creating Objects in RT
111
112 If you need to have users create a group, scrip, template, or some other
113 object in their RT instance, you can automate this using an F<initialdata>
114 file. If you need this, the file should go in the C<etc> directory. This will
115 allow users to easily run the F<initialdata> file when installing with:
116
117     make initdb
118
119 =head2 Module::Install Files
120
121 As mentioned above, the RT extension tools are set up to use L<Module::Install>
122 to manage the distribution. When you run
123
124     perl Makefile.PL
125
126 for the first time, L<Module::Install> will create an C<inc> directory for all
127 of the files it needs. Since you are the author, a C<.author> directory
128 (note the . in the directory name) is created for you in the C<inc>
129 directory. When L<Module::Install> detects this directory, it does things only
130 the author needs, like pulling in modules to put in the C<inc> directory.
131 Once you have this set up, L<Module::Install> should mostly do the right thing.
132 You can find details in the module documentation.
133
134 =head2 Tests
135
136 =head3 Test Directory
137
138 You can create tests for your new extension just as with other perl code
139 you write. However, unlike typical CPAN modules where users run the tests
140 as a step in the installation process, RT users installing extensions don't
141 usually run tests. This is because running the tests requires your RT to
142 be set up in development mode which involves installing some additional
143 modules and having a test database. To prevent users from accidentally
144 running the tests, which will fail without this testing setup, we put them in
145 a C<xt> directory rather than the typical C<t> directory.
146
147 =head3 Writing Extension Tests
148
149 If you want to write and run tests yourself, you'll need a development RT
150 instance set up. Since you are building an extension, you probably already have
151 one. To start with testing, set the C<RTHOME> environment variable to the base
152 directory of your RT instance so your extension tests run against the right
153 instance. This is especially useful if you have your test RT installed in a non-standard location.
154
155 Next, you need to subclass from L<RT::Test>
156 which gives you access to the test RT and a test database for running
157 tests. For this, you'll create a F<Test.pm> file in your C<lib> tree.
158 The easiest way to set up the test module to pull in F<RT::Test> is to look at
159 an example extension. L<RT::Extension::RepeatTicket>, for example, has a
160 testing configuration you can borrow from.
161
162 You'll notice that the file included in the extension is
163 F<lib/RT/Extension/RepeatTicket/Test.pm.in>. This is because there are paths
164 that are set based on your RT location, so the actual F<Test.pm> file is
165 written when you run F<Makefile.PL> with appropriate paths substituted
166 when F<Makefile.PL> is run. L<Module::Install> provides an interface to make
167 this easy with a C<substitute> feature. The substitution code is in the
168 F<Makefile.PL> file and you can borrow that as well.
169
170 Once you have that set up, add this to the top of your test files:
171
172     use RT::Extension::Demo::Test tests => undef;
173
174 and you'll be able to run tests in the context of a fully functioning RT
175 instance. The L<RT::Test>
176 documentation describes some of the helper methods available and you can
177 look at other extensions and the RT source code for examples of how to
178 do things like create tickets, queues, and users, how to set rights, and
179 how to modify tickets to simulate various RT tasks.
180
181 If you have a command-line component in your extension, the easiest way
182 to test it is to set up a C<run> method using the Modulino approach.
183 You can find an example of this approach in L<RT::Extension::RepeatTicket>
184 in the F<bin> directory.
185
186 =head2 Patches
187
188 If you need to provide patches to RT for any reason, you can put them in
189 a C<patches> directory. See L</"Changes to RT"> for more information.
190
191 =head1 Callbacks
192
193 The RT codebase, mostly the Mason templates, contains hooks called callbacks
194 that make it easy to add functionality without changing the RT code itself.
195 RT invokes callbacks by looking in the source directories for files that might
196 have extra code.
197
198 =head2 Directory Structure
199
200 RT looks in the F<local/plugins> directory under the RT base directory for
201 extensions registered with the C<@Plugins> configuration. RT then uses the
202 following structure when looking for callbacks:
203
204     local/plugins/[ext name]/html/Callbacks/[custom name]/[rt mason path]/[callback name]
205
206 The extension installation process will handle some of this for you by putting
207 your html directory under F<local/plugins/[ext name]> as part of the
208 installation process. You need to make sure the path under C<html> is correct
209 since that is installed as-is.
210
211 The C<Callbacks> directory is required. The next directory can be named
212 anything and is provided to allow RT owners to keep local files organized
213 in a way that makes sense to them. In the case of
214 an extension, you should name the directory the same as your extension.
215 So if your extension is C<RT::Extension::Demo>, you should create a
216 F<RT-Extension-Demo> directory under F<Callbacks>.
217
218 The rest of the path is determined by the RT Mason code and the callback you
219 want to use. You can find callbacks by looking for calls to the C<callback>
220 method in the RT Mason code. You can use something like this in your base
221 RT directory:
222
223     # find share/html/ | xargs grep '\->callback'
224
225 As an example, assume you wanted to modify the ticket update page to put
226 something after the Time Worked field. You run the above and see there is
227 a callback in F<share/html/Ticket/Update.html> that looks like this:
228
229     $m->callback( %ARGS, CallbackName => 'AfterWorked', Ticket => $TicketObj );
230
231 You look at the F<Update.html> file and see that the callback is located
232 right after the Time Worked field. To add some code that RT will
233 run at that point, you would create the directory:
234
235     html/Callbacks/RT-Extension-Demo/Ticket/Update.html/
236
237 Note that F<Update.html> is a file in the RT source, but it becomes a directory
238 in your extension code. You then create a file with the name of the
239 callback, in this case F<AfterWorked>, and that's where you put your code.
240 So the full path and file would be:
241
242     html/Callbacks/RT-Extension-Demo/Ticket/Update.html/AfterWorked
243
244 If you see a callback that doesn't have a C<CallbackName> parameter, name
245 your file F<Default> and it will get invoked since that is the default
246 callback name when one isn't provided.
247
248 =head2 Callback Parameters
249
250 When you look at callbacks using the method above, the other important
251 thing to consider is the parameter list. In addition to the C<CallbackName>,
252 the other parameters listed in the callback will be passed to you
253 to use as you develop your extension.
254
255 Getting these parameters is important because you'll likely need them
256 in your code, getting data from the current ticket object, for example.
257 These values are also often passed by reference, which allows you to modify
258 them, potentially changing the behavior of the RT template when it
259 continues executing after evaluating your code.
260
261 Some examples are adding a C<Limit> call to modify search results on
262 a L<DBIx::SearchBuilder> object, or setting a flag like C<$skip_update>
263 for a callback like this:
264
265     $m->callback( CallbackName => 'BeforeUpdate', ARGSRef => \%ARGS, skip_update => \$skip_update,
266               checks_failure => $checks_failure, results => \@results, TicketObj => $TicketObj );
267
268 There are many different callbacks in RT and these are just a few examples
269 to give you idea what you can do in your callback code. You can also look
270 at other extensions for examples of how people use callbacks to modify
271 and extend RT.
272
273 =head1 Adding and Modifying Menus
274
275 You can modify all of RT's menus using callbacks as described in L</Callbacks>.
276 The file in RT that controls menus is:
277
278     share/html/Elements/Tabs
279
280 and you'll find a Privileged and SelfService callback which gives you access
281 to those two sets of menus. In those callbacks, you can add to or change
282 the main menu, the page menu, or the page widgets.
283
284 You can look at the F<Tabs> file itself for examples of adding menu items.
285 The menu object is a L<RT::Interface::Web::Menu> and you can find details on
286 the available parameters in the documentation.
287
288 Here are some simple examples of what you might do in a callback:
289
290     <%init>
291     # Add a brand new root menu item
292     my $bps = Menu()->child(
293         'bps', # any unique identifier
294         title => 'Corporate',
295         path  => 'http://bestpractical.com'
296     );
297
298     #Add a submenu item to this root menu item
299     $bps->child(
300         'wiki',
301         title => 'Wiki',
302         path  => 'http://wiki.bestpractical.com',
303     );
304
305     #Retrieve the 'actions' page menu item
306     if (my $actions = PageMenu->child('actions')) {
307         $actions->child(
308             'newitem',
309             title => loc('New Action'), path => '/new/thing/here',
310         )
311     }
312     </%init>
313
314 =head1 Changes to RT
315
316 When writing an extension, the goal is to provide all of the new functionality
317 in your extension code using standard interfaces into RT. However,
318 sometimes when you're working on an extension, you'll find you really need
319 a change in RT itself to make your extension work. Often this is something
320 like adding a new callback or a method to a core module that would be
321 helpful for everyone.
322
323 Since any change to RT will only be included in the next version and
324 forward, you'll need to provide something for users on current or older
325 versions of RT. An easy way to do this is to provide a patch in your
326 extension distribution. In general, you should only provide patches
327 if you know they will eventually be merged into RT. Otherwise, you
328 may have to provide versions of your patches for each release of RT.
329 You can read more about getting changes accepted into RT in the
330 L<hacking> document. We generally accept patches that add new callbacks.
331
332 Create a C<patches> directory in your extension distribution to hold
333 your patch files. Name the patch files with the latest version of RT
334 that needs the patch. For example, if the patch is needed for RT 4.0.7,
335 name your patch C<4.0.7-some-patch.diff>. That tells users that if they
336 are using RT 4.0.7 or earlier, they need to apply the patch. If your
337 extension can be used for RT 3.8, you'll likely need to provide different
338 patches using the same naming convention.
339
340 Also remember to update your install documentation to remind users to apply
341 the patch.
342
343 =head1 Preparing for CPAN
344
345 When you have your extension ready and want to release it to the world, you
346 can do so with a few simple steps.
347
348 Assuming you have run C<perl Makefile.PL> and you created the F<inc/.author>
349 directory as described above, a F<README> file will be created for you. You can
350 now type:
351
352     make manifest
353
354 and a F<MANIFEST> file will be created. It should contain all of the needed
355 to install and run your extension. If you followed the steps above, you'll have
356 also have a F<inc> directory which contains L<Module::Install> code. Note that
357 this code should also be included with your extension when you release it as
358 it's part of the install process.
359
360 Next, check to see if everything is ready with:
361
362     make distcheck
363
364 If anything is missing, it will be reported and you can go fix it.
365 When the check is clean, run:
366
367     make dist
368
369 and a new distribution will be created in the form of a tarred and gzipped
370 file.
371
372 Now you can upload to cpan with the F<cpan-upload> utility provided by
373 L<CPAN::Uploader> or your favorite method of uploading to CPAN.
374
375 =cut
376