[Rt-commit] rt branch, 4.0/writing-rt-extensions-doc, repushed
Jim Brandt
jbrandt at bestpractical.com
Thu Jan 16 11:01:44 EST 2014
The branch 4.0/writing-rt-extensions-doc was deleted and repushed:
was 09d9455fb36279a0135e87815d9414e17a3c5331
now 5e45b9ab80dd658273621d20e5e62a835ae56b31
1: 1b9e5a2 ! 1: 5e45b9a New doc for writing RT extensions
@@ -1,6 +1,6 @@
Author: Jim Brandt <jbrandt at bestpractical.com>
- New doc for writing extensions
+ New doc for writing RT extensions
diff --git a/docs/writing-extensions.pod b/docs/writing-extensions.pod
new file mode 100644
@@ -43,9 +43,9 @@
+=back
+
+If this is your first time using L<Dist::Zilla>, you can set up your
-+CPAN detals by running:
-+
-+ # dzil setup
++CPAN details by running:
++
++ dzil setup
+
+You can read about L<Dist::Zilla> and the C<dzil> command at L<http://dzil.org>.
+
@@ -53,7 +53,7 @@
+extension and run the following, replacing Demo with a descriptive name
+for your new extension:
+
-+ # dzil new -P RTx RT-Extension-Demo
++ dzil new -P RTx RT-Extension-Demo
+
+You'll see something like:
+
@@ -62,11 +62,11 @@
+ [DZ] dist minted in ./RT-Extension-Demo
+
+If you're stuck on a name, take a look at some of the existing RT extensions.
-+You can also ask around the office or on IRC to see what people thinks
++You can also ask around IRC (#rt on irc.perl.org) to see what people think
+makes sense for what the extension will do.
+
+You'll now have a directory with the basic files for your extension.
-+Included is a gitignore file which is handy if you use git for your version
++Included is a F<gitignore> file, which is handy if you use git for your version
+control like we do. If you don't use git, feel free to delete it, but we hope
+you're using some sort of version control for your work.
+
@@ -74,7 +74,8 @@
+
+There are several places to put code to provide your new features
+and if you follow the guidelines below, you'll make sure things
-+get installed in the right places when you're ready to use it.
++get installed in the right places when you're ready to use it. These standards
++apply to RT 4.0 and 4.2 and any differences between the two are noted below.
+
+=head2 Module Code
+
@@ -93,43 +94,108 @@
+Your Mason templates should go in an C<html> directory with the appropriate
+directory structure to make sure the callbacks are executed.
+
++If you are creating completely new pages for RT, you can put these under the
++C<html> directory also. You can create subdirectories as needed to add the
++page to existing RT paths (like Tools) or to create new directories for your
++extension.
++
++=head2 CSS and Javascript
++
++Where these files live differs between RT 4.2 and above, and RT 4.0 and
++below; if you need your extension to be compatible with both, you may
++need to provide both configurations. On RT 4.2 and above, create a
++C<static> directory at the top level under your extension, and under
++that a C<css> directory and a C<js> directory. Before RT 4.2, you should
++create C<css> and C<js> directories in C<html/NoAuth/>.
++
++To add files to RT's include paths, you can use the L<RT/AddStyleSheets> and
++L<RT/AddJavascript> methods available in the L<RT> module. You can put the
++lines near the top of your module code (in your "Demo.pm" file). If you set up
++the paths correctly, you should only need to set the file names like this:
++
++ RT->AddStyleSheets('myextension.css');
++ RT->AddJavaScript('myextension.js');
++
+=head2 Creating Objects in RT
+
+If you need to have users create a group, scrip, template, or some other
-+object in their RT instance, you can automate this using an C<initialdata>
-+file. If you need this, the file should go in the C<etc> directory.
-+See also L<docs/initialdata>.
++object in their RT instance, you can automate this using an F<initialdata>
++file. If you need this, the file should go in the C<etc> directory. This will
++allow users to easily run the F<initialdata> file when installing with:
++
++ make initdb
+
+=head2 Module::Install Files
+
+As mentioned above, the RT extension tools are set up to use L<Module::Install>
+to manage the distribution. When you run
+
-+ # perl Makefile.PL
++ perl Makefile.PL
+
+for the first time, L<Module::Install> will create an C<inc> directory for all
-+of the files it needs. Since you are the author, you should create a
-+C<.author> directory (note the . in the directory name) in the C<inc>
++of the files it needs. Since you are the author, a C<.author> directory
++(note the . in the directory name) is created for you in the C<inc>
+directory. When L<Module::Install> detects this directory, it does things only
+the author needs, like pulling in modules to put in the C<inc> directory.
-+Once you have this set up, L<Module::Install> should mostly do the right thing,
-+but you can read more about it in the module documentation.
++Once you have this set up, L<Module::Install> should mostly do the right thing.
++You can find details in the module documentation.
+
+=head2 Tests
++
++=head3 Test Directory
+
+You can create tests for your new extension just as with other perl code
+you write. However, unlike typical CPAN modules where users run the tests
+as a step in the installation process, RT users installing extensions don't
+usually run tests. This is because running the tests requires your RT to
+be set up in development mode which involves installing some additional
-+modules and having a dev instance. To prevent users from accidentally
-+running the tests, we put them in a C<xt> directory rather than the typical
-+C<t> directory.
++modules and having a test database. To prevent users from accidentally
++running the tests, which will fail without this testing setup, we put them in
++a C<xt> directory rather than the typical C<t> directory.
++
++=head3 Writing Extension Tests
++
++If you want to write and run tests yourself, you'll need a development RT
++instance set up. Since you are building an extension, you probably already have
++one. To start with testing, set the C<RTHOME> environment variable to the base
++directory of your RT instance so your extension tests run against the right
++instance. This is especially useful if you have your test RT installed in a non-standard location.
++
++Next, you need to subclass from L<RT::Test>
++which gives you access to the test RT and a test database for running
++tests. For this, you'll create a F<Test.pm> file in your C<lib> tree.
++The easiest way to set up the test module to pull in F<RT::Test> is to look at
++an example extension. L<RT::Extension::RepeatTicket>, for example, has a
++testing configuration you can borrow from.
++
++You'll notice that the file included in the extension is
++F<lib/RT/Extension/RepeatTicket/Test.pm.in>. This is because there are paths
++that are set based on your RT location, so the actual F<Test.pm> file is
++written when you run F<Makefile.PL> with appropriate paths substituted
++when F<Makefile.PL> is run. L<Module::Install> provides an interface to make
++this easy with a C<substitute> feature. The substitution code is in the
++F<Makefile.PL> file and you can borrow that as well.
++
++Once you have that set up, add this to the top of your test files:
++
++ use RT::Extension::Demo::Test tests => undef;
++
++and you'll be able to run tests in the context of a fully functioning RT
++instance. The L<RT::Test>
++documentation describes some of the helper methods available and you can
++look at other extensions and the RT source code for examples of how to
++do things like create tickets, queues, and users, how to set rights, and
++how to modify tickets to simulate various RT tasks.
++
++If you have a command-line component in your extension, the easiest way
++to test it is to set up a C<run> method using the Modulino approach.
++You can find an example of this approach in L<RT::Extension::RepeatTicket>
++in the F<bin> directory.
+
+=head2 Patches
+
+If you need to provide patches to RT for any reason, you can put them in
-+a C<patches> directory. See L<'Changes to RT'> for more information.
++a C<patches> directory. See L</"Changes to RT"> for more information.
+
+=head1 Callbacks
+
@@ -140,21 +206,23 @@
+
+=head2 Directory Structure
+
-+RT looks in the following path for callbacks:
-+
-+ rt_base/local/html/Callbacks/[custom_name]/[rt mason path]/[callback name]
++RT looks in the F<local/plugins> directory under the RT base directory for
++extensions registered with the C<@Plugins> configuration. RT then uses the
++following structure when looking for callbacks:
++
++ local/plugins/[ext name]/html/Callbacks/[custom name]/[rt mason path]/[callback name]
+
+The extension installation process will handle some of this for you by putting
-+your html directory under C<local> as part of the installation process.
-+You need to make sure the path under C<html> is correct since that is installed
-+as-is.
++your html directory under F<local/plugins/[ext name]> as part of the
++installation process. You need to make sure the path under C<html> is correct
++since that is installed as-is.
+
+The C<Callbacks> directory is required. The next directory can be named
+anything and is provided to allow RT owners to keep local files organized
+in a way that makes sense to them. In the case of
+an extension, you should name the directory the same as your extension.
+So if your extension is C<RT::Extension::Demo>, you should create a
-+C<RT-Extension-Demo> directory under C<Callbacks>.
++F<RT-Extension-Demo> directory under F<Callbacks>.
+
+The rest of the path is determined by the RT Mason code and the callback you
+want to use. You can find callbacks by looking for calls to the C<callback>
@@ -165,25 +233,25 @@
+
+As an example, assume you wanted to modify the ticket update page to put
+something after the Time Worked field. You run the above and see there is
-+a callback in C<share/html/Ticket/Update.html> that looks like this:
++a callback in F<share/html/Ticket/Update.html> that looks like this:
+
+ $m->callback( %ARGS, CallbackName => 'AfterWorked', Ticket => $TicketObj );
+
-+You look at the Update.html file and see that the callback is located
++You look at the F<Update.html> file and see that the callback is located
+right after the Time Worked field. To add some code that RT will
+run at that point, you would create the directory:
+
+ html/Callbacks/RT-Extension-Demo/Ticket/Update.html/
+
-+Note that Update.html is a file in the RT source, but it becomes a directory
++Note that F<Update.html> is a file in the RT source, but it becomes a directory
+in your extension code. You then create a file with the name of the
-+callback, in this case C<AfterWorked>, and that's where you put your code.
++callback, in this case F<AfterWorked>, and that's where you put your code.
+So the full path and file would be:
+
+ html/Callbacks/RT-Extension-Demo/Ticket/Update.html/AfterWorked
+
+If you see a callback that doesn't have a C<CallbackName> parameter, name
-+your file C<Default> and it will get invoked since that is the default
++your file F<Default> and it will get invoked since that is the default
+callback name when one isn't provided.
+
+=head2 Callback Parameters
@@ -210,6 +278,47 @@
+to give you idea what you can do in your callback code. You can also look
+at other extensions for examples of how people use callbacks to modify
+and extend RT.
++
++=head1 Adding and Modifying Menus
++
++You can modify all of RT's menus using callbacks as described in L</Callbacks>.
++The file in RT that controls menus is:
++
++ share/html/Elements/Tabs
++
++and you'll find a Privileged and SelfService callback which gives you access
++to those two sets of menus. In those callbacks, you can add to or change
++the main menu, the page menu, or the page widgets.
++
++You can look at the F<Tabs> file itself for examples of adding menu items.
++The menu object is a L<RT::Interface::Web::Menu> and you can find details on
++the available parameters in the documentation.
++
++Here are some simple examples of what you might do in a callback:
++
++ <%init>
++ # Add a brand new root menu item
++ my $bps = Menu()->child(
++ 'bps', # any unique identifier
++ title => 'Corporate',
++ path => 'http://bestpractical.com'
++ );
++
++ #Add a submenu item to this root menu item
++ $bps->child(
++ 'wiki',
++ title => 'Wiki',
++ path => 'http://wiki.bestpractical.com',
++ );
++
++ #Retrieve the 'actions' page menu item
++ if (my $actions = PageMenu->child('actions')) {
++ $actions->child(
++ 'newitem',
++ title => loc('New Action'), path => '/new/thing/here',
++ )
++ }
++ </%init>
+
+=head1 Changes to RT
+
@@ -227,10 +336,10 @@
+if you know they will eventually be merged into RT. Otherwise, you
+may have to provide versions of your patches for each release of RT.
+You can read more about getting changes accepted into RT in the
-+L<docs/hacking> document.
++L<hacking> document. We generally accept patches that add new callbacks.
+
+Create a C<patches> directory in your extension distribution to hold
-+your patch files. Name the patch files with the latest verison of RT
++your patch files. Name the patch files with the latest version of RT
+that needs the patch. For example, if the patch is needed for RT 4.0.7,
+name your patch C<4.0.7-some-patch.diff>. That tells users that if they
+are using RT 4.0.7 or earlier, they need to apply the patch. If your
@@ -240,19 +349,37 @@
+Also remember to update your install documentation to remind users to apply
+the patch.
+
++=head1 Preparing for CPAN
++
++When you have your extension ready and want to release it to the world, you
++can do so with a few simple steps.
++
++Assuming you have run C<perl Makefile.PL> and you created the F<inc/.author>
++directory as described above, a F<README> file will be created for you. You can
++now type:
++
++ make manifest
++
++and a F<MANIFEST> file will be created. It should contain all of the needed
++to install and run your extension. If you followed the steps above, you'll have
++also have a F<inc> directory which contains L<Module::Install> code. Note that
++this code should also be included with your extension when you release it as
++it's part of the install process.
++
++Next, check to see if everything is ready with:
++
++ make distcheck
++
++If anything is missing, it will be reported and you can go fix it.
++When the check is clean, run:
++
++ make dist
++
++and a new distribution will be created in the form of a tarred and gzipped
++file.
++
++Now you can upload to cpan with the F<cpan-upload> utility provided by
++L<CPAN::Uploader> or your favorite method of uploading to CPAN.
++
+=cut
+
-+# TODO:
-+
-+# Add docs about how to set up testing
-+
-+# Add docs about final distribution steps (make dist, etc.)
-+
-+# Make sure link to initialdata.pod works
-+
-+# Add docs about best way to extend email handling with
-+# mail plugins
-+
-+# Add info on creating additional web pages in html, where
-+# they are installed, and how to add to the RT menu
-
2: e7d1df9 < -: ------- Fix internal pod link
3: 76b0cde < -: ------- Update .author directory details
4: e6c5c35 < -: ------- Add details on setting up the static directory.
5: 38f4250 < -: ------- Minor nits
6: 72d37ff < -: ------- Add docs on writing tests and clean up callback path example
7: abafcef < -: ------- Add docs on final CPAN release steps
8: f3565c2 < -: ------- Add menu example plus some link cleanup
9: 09d9455 < -: ------- Spellcheck
More information about the rt-commit
mailing list