summaryrefslogtreecommitdiff
path: root/rt/docs/extending
diff options
context:
space:
mode:
Diffstat (limited to 'rt/docs/extending')
-rw-r--r--rt/docs/extending/clickable_links.pod184
-rw-r--r--rt/docs/extending/external_custom_fields.pod90
-rw-r--r--rt/docs/extending/using_forms_widgets.pod113
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.
+