diff options
Diffstat (limited to 'rt/docs/extending')
-rw-r--r-- | rt/docs/extending/clickable_links.pod | 184 | ||||
-rw-r--r-- | rt/docs/extending/external_custom_fields.pod | 90 | ||||
-rw-r--r-- | rt/docs/extending/using_forms_widgets.pod | 113 |
3 files changed, 387 insertions, 0 deletions
diff --git a/rt/docs/extending/clickable_links.pod b/rt/docs/extending/clickable_links.pod new file mode 100644 index 000000000..91e9eec22 --- /dev/null +++ b/rt/docs/extending/clickable_links.pod @@ -0,0 +1,184 @@ +=head1 MakeClicky extension + +=head2 Description + +I<MakeClicky> detects various formats of data in headers and email +messages, and makes them into links in RT's web UI. + +=head2 Configuration + +You can configure which actions are enabled from RT config with the +@Active_MakeClicky option, which should contain an ordered list of the +actions you want to apply. + +By default, RT provides two actions: + +=over 4 + +=item C<httpurl> + +Detects C<http://> and C<https://> URLs and adds an C<[Open URL]> link +after the URL. + +=item C<httpurl_overwrite> + +Detects URLs as C<httpurl> format, but replaces the URL with a link. + +=back + +RTIR, an RT extension for CERT teams (not installed with core RT), +shipps with several additional actions you can use: C<ip>, C<ipdecimal>, +C<email>, C<domain> and C<RIPE>. + +=head2 Order of actions + +The order of the actions is important in situations when you use +multiple actions that could match the same block of text; only the first +matching action from the list is applied. For example, it makes no sense +to use C<httpurl> and C<httpurl_overwrite> at the same time, as both +actions always match the same pieces of text. + +=head2 How it works + +Each action consists of regular expression and function that does text +replacement. When you open the history of a ticket, RT searches in the +text with the given regular expresion for matches. If it finds a match, +it calls the function with the match as the argument, then replaces the +matched text with the string returned by the function. + +While RT only searches plaintext content, the actions can generate +arbitrary HTML. + +=head2 Writing custom MakeClicky actions + +To extend the list of actions with your own types of data, use the +provided callback. Specifically, create the file +F<local/html/Callbacks/MyCallbacks/Elements/MakeClicky/Default>. + +It will be called with the following arguments: + +=over 4 + +=item types + +An array reference of hash references. Modify this array +reference to add your own types; the first matching type will be +used. Each hashref should contain: + +=over 4 + +=item name + +The name of the data format; this is used in the configuration file to +enable the format. + +=item regex + +A regular expression to match against. + +=item action + +The name of the action to run (see "actions", below) + +=back + +=item actions + +A hash reference of 'actions'. Modify this hash reference to change or +add action types. Values are subroutine references which will get +called when needed. They should return the modified string. Note that +subroutine B<must escape> HTML. + +=item handler + +A subroutine reference; modify it only if you have to. This can be used +to add pre- or post-processing around all actions. + +=back + +=head2 Actions' arguments + +A hash is passed to the action with two keys that always exist: + +=over 4 + +=item value + +The full match of the regular expression; this is the block of text that +will be replaced with action's result. + +=item all_matches + +And arrayref with all of the match's capturing groups; for example if +your regexp is C<qr{ticket\s+#(\d+)}>, then the first element will be +full match ("ticket #XXX"), the same as in 'value' key, but the second +element of the array will be the id of a ticket (XXX). Using this, you +can avoid reparsing the value in the action. Only the first eight +groups of your regexps are passed to action. + +=back + +=head2 Custom MakeClicky action example + +Create a new file F</opt/rt4/local/html/Callbacks/MyCallbacks/Elements/MakeClicky/Default> +with the content: + + <%ARGS> + $types => [] + $actions => {} + </%ARGS> + <%INIT> + my $web_path = RT->Config->Get('WebPath'); + + # action that takes ticket ID as argument and returns link to the ticket + $actions->{'link_ticket'} = sub { + my %args = @_; + my $id = $args{'all_matches'}[1]; + return qq{<a href="$web_path/Ticket/Display.html?id=$id">$args{value}</a>}; + }; + + # add action to the list + push @$types, { + # name, that should be used in config to activate action + name => 'short_ticket_link', + # regular expression that matches text 'ticket #xxx' + regex => qr{ticket\s+#(\d+)}i, + # name of the action that should be applied + action => 'link_ticket', + }; + </%INIT> + +That's all; add C<short_ticket_link> to the C<@Active_MakeClicky> option +in your C<RT_SiteConfig.pm>, and restart your server. Creating a ticket +with "ticket #1" in the body should cause that text to be automatically +linked to the ticket in question. + +=head2 Notes for custom clicky actions writers + +=over + +=item * + +Note that an action B<must escape> illegal HTML characters with entities +and/or arguments in URLs. + +=item * + +Complex regular expressions could slow down RT, as the conversion is run +each time a user opens a ticket, for every transaction. For long +tickets and complex regular expressions, this can slow down ticket +display notably. + +=item * + +Try to match the shortest expression you need with your regular +expression; otherwise another action may miss its chance to match. + +=item * + +Whenever possible, precalculate values using closures around the +functions. + +=back + +=cut diff --git a/rt/docs/extending/external_custom_fields.pod b/rt/docs/extending/external_custom_fields.pod new file mode 100644 index 000000000..c6730ae4e --- /dev/null +++ b/rt/docs/extending/external_custom_fields.pod @@ -0,0 +1,90 @@ +=head1 External custom fields + +=head2 Description + +C<External custom fields> is an extension to custom fields that allow +you to define CFs with dynamic lists of values. Loading values into +these custom fields requires writing a little Perl code to fetch the +data from the external source. + +=head2 Introduction into writing source of values + +For each type of data source that you want, you'll need to put a file in +F</opt/rt4/local/lib/RT/CustomFieldValues/> (or equivalent if you +installed RT into someplace other than F</opt/rt4>). To get a sense of +the code that you'll need to write, take a look at the code in +L</opt/rt4/lib/RT/CustomFieldValues/Groups.pm> for a simple example +which just uses RT's API to pull in a list of RT's groups. + +Running C<perldoc /opt/rt4/lib/RT/CustomFieldValues/External.pm> will +show you the documentation for the API that needs to be fulfilled; +copying and editing the C<Groups> example is probably a fine place to +start. + +Later in this doc we'll describe the example a little bit more. + +=head2 Configuration + +After the custom code is written, you need to tell RT about its +existence by adding something like following to your RT_SiteConfig.pm: + + Set(@CustomFieldValuesSources, "RT::CustomFieldValues::MySource"); + +The value in quotes should be the name of the class that you created. + +Stop and start your web server to enable any config changes. Open the +web interface as an administrative user (such as root), and create new +custom field. Set its type to be a Select or Autocomplete field, and +save the changes. You should now you have ability to select a "source" +for values. Choose the class you wrote from the list and the save +changes. + +=head2 How to write custom source + +You have to implement a subclass of L<RT::CustomFieldValues::External>. +There are two main methods you want to override: + +=over 4 + +=item SourceDescription + +This method should return a string describing the data source; this is +the identifier which the administrator will see in the dropdown in the +web interface. See L</Configuration>. + +=item ExternalValues + +This method should return an array reference of hash references. The +hash references should contain keys for C<name>, C<description>, and +C<sortorder>. C<name> is most important one; the others are optional. + +=back + +Here's a simple static example: + + package RT::CustomFieldValues::MySource; + + # define class inheritance + use base qw(RT::CustomFieldValues::External); + + # admin friendly description, the default valuse is the name of the class + sub SourceDescription { + return 'My Source'; + } + + # actual values provider method + sub ExternalValues { + # return reference to array ([]) + return [ + # each element of the array is a reference to hash that describe a value + # possible keys are name, description and sortorder + { name => 'value1', description => 'external value', sortorder => 1 }, + { name => 'value2', description => 'another external value', sortorder => 2 }, + # values without description are also valid, the default description is empty string + { name => 'value3', sortorder => 3 }, + # you can skip sortorder too, but note that the default sortorder is 0 (zero) + { name => 'value3' }, + ]; + } + + 1; # don't forget to return some true value diff --git a/rt/docs/extending/using_forms_widgets.pod b/rt/docs/extending/using_forms_widgets.pod new file mode 100644 index 000000000..8deb91362 --- /dev/null +++ b/rt/docs/extending/using_forms_widgets.pod @@ -0,0 +1,113 @@ +=head1 Using widgets F<html/Widgets/Form*> + +This widgets was implemented to address several common issues in handling +request arguments and allow developers to avoid reinventing the wheel. + +=head2 General info + +Each component shows widget by default and has two methods: Process and +InputOnly. The first one method process arguments and return new value +of a parametr. The second one is helper that shows only form elements +with minimum of required text labels. + +So you show a widget with: + <& /Widgets/Form/Integer, + Name => 'NameOfInputElement', + Description => 'Input integer', + &> + +You can show only C<input> box using: + <& /Widgets/Form/Integer:InputOnly, + Name => 'NameOfInputElement', + &> + +In such a simple case you even can avoid processing. Yeah, most probably +you want to check if value is really integer, but these widgets don't +do validation for you, but they are more about fetching values from +hash of arguments, showing these values to user and preserving state +of value between form reloads (see below). + +=head2 Processing + +Processing is required when you use L<extended features|/Extendent features>, +such as Default, Multiple or Alternative. + +To process arguments of a request you have to do the following: + $ARGS{'NameOfInputElement'} = $m->comp( + '/Widgets/Form/Integer:Process', + Arguments => \%ARGS, + Name => 'NameOfInputElement', + ); + +The method returns processed value in canonical form. For different widgets +a canonical form is different and depends on activated features, so you must +always activate the same features during showing a widget and processing +results. + +=head2 Extendent features + +=head3 Default value + +If C<Default> argument is true then widgets expect that there is some +default value for argument if user fills nothing. 'Nothing' in each +widget is different, for example in select box it's special option +which is always the first one, in integer box string '' means empty +value, but boolean box uses radio buttons in this case with three +options: Yes, No and Default. + +Each widget that supports C<Default> feature as well has C<DefaultLabel> and +C<DefaultValue> arguments. + +=head4 Processing and showing with activated Default feature + +When this option is activated then C<Process> method returns undef +value if user selected default value. So for integer box it's empty +string and so on. + +As well when you show a widget you should pass undef as C<CurrentValue> +to inform widget that the current value is default one. + +As all methods of a widget are consistent in this behaviour so you +shouldn't care much about that, but this allows you to implement +custom actions if processing returned undef, for example delete user's +preference record instead of updating it (default value may change later to). + +=head4 C<DefaultValue> when C<Default> is not active + +DefaultValue argument is still actual in the Process method even if +C<Default> is not true. This argument defines intial value. If value +of a key in Arguments is not defined then it's treated as intial state +and the method returns default value. + +=head3 Multiple and Alternative + +These options are only supported by the select widget. + +TODO: Add more info + +=head2 Implementation details + +=head3 Boolean widget + +This widget a little bit tricky. When you use Default option then +things are simple and you see three radio buttons, but in other +case we use a checkbox. But as you know browsers don't pass unchecked +boxes to server, so arguments of a request has no entry for them. + +In the latter case it's hard to figure out case when user unselected +value. Imagine form with a checkbox, you want show it checked by +default and as well form is reloadable (like Reply forms that have +"Add Another File" buttons). User uncheck the box and then upload +file, in this case you want to show user's choice instead of default, +but browser doesn't send any value and you can not figure out if +it's initial state or page reload. To solve this problem we use magic +hidden input field with the same name as the box and value equal to +zero (0). Mason folds arguments with the same name into array refs, so +we get 0 if box is unchecked and [0, 1] if box is checked. An array +reference is true value and 0 is defined value so we know that it's +not initial state and avoid switching back to default. As well this +trick works good in a case when you want show a link to a page and +define default choice for some boolean argument, you don't need +to set argument twice, you just set it to true value (for ex. 1) and +things just work. + |