https://rt-wiki.bestpractical.com/api.php?action=feedcontributions&user=Alexmv&feedformat=atomRequest Tracker Wiki - User contributions [en]2024-03-29T15:33:07ZUser contributionsMediaWiki 1.37.2https://rt-wiki.bestpractical.com/index.php?title=WorkFlow&diff=26604WorkFlow2018-08-16T01:50:53Z<p>Alexmv: </p>
<hr />
<div>= Modeling Workflow in RT =<br />
<br />
We had the requirement to make sure that when equipment was ordered for installation all necessary tasks were completed by the involved parties. Creating a single ticket and assigning it to each responsible party in turn had the problem of serializing the tasks, not giving advance warning, and not having a predetermined checklist of things to do. This is a fairly simple attempt to model a planned workflow in RT, using a master ticket for the installation, and child tickets for the subtask.<br />
<br />
== Queue Setup ==<br />
<br />
First, set up a new queue, which we'll call Installation. Nothing special here. Make sure that the correct people have permission to update the queue including the permission to set custom fields.<br />
<br />
== Custom Field Setup ==<br />
<br />
Next, create a custom field for each subtask. The name of the field should suggest the subtask to the reader, because the field description doesn't show up anyplace useful. We created custom fields 'Order Status', 'Power', 'DNS', 'Rack', 'Array', and '[[OSInstall]]'. Make each of type 'Select one value', which will make them into drop-down lists. For 'Order Status', we're using values such as 'Requirements', 'In Purchasing', 'Vendor', and 'Delivered' to indicate the status of the order. For the other fields we use the simple values 'Required' and 'Completed'. (We are still exploring whether the complexity of additional status values for these are useful.) The 'Required' status will become magic in the next steps. Go back to the Installation queue and enable these custom fields.<br />
<br />
== Template Setup ==<br />
<br />
Now we need some templates for the child tickets. These determine what will be in the child tickets when they are first created. Create them in the Installation queue; they don't need to be global. Name them something related to the subtask name for your sanity, but the name itself doesn't link them. Here is the [[PowerTicket]] template:<br />
<br />
<pre><br />
===Create-Ticket: power<br />
Subject: Power::{$Tickets{'TOP'}->Subject}<br />
Parents: TOP<br />
Queue: Installation<br />
Owner: smith<br />
AdminCc: jones<br />
Content: Power requirements are needed for this equipment.<br />
Please see the parent ticket for details.<br />
ENDOFCONTENT<br />
</pre><br />
<br />
The Owner of the ticket should be someone already defined in your RT database who will be responsible for the subtask, in this example smith. The [[AdminCc]] can be used for that person's backup or other interested parties. As of RT 3.4.2, both of these have to be users, not groups. It would be very nice if groups worked, to allow more dynamic updating of responsibilities if someone is away. Notification of the new owners is taken care of by the normal Create Ticket actions in the queue.<br />
<br />
The Queue is the queue in which the new ticket is created. At the moment, we've elected to create them in the same queue as the parent ticket, but it may make more sense to create them in other queues. The Parent link will keep everything tied together if you do.<br />
<br />
== Action Setup ==<br />
<br />
Now we can glue the templates to the custom fields. To make this clean, I created a custom Condition, installed in $RTHOME/local/lib/RT/Condition/[[FieldRequired]].pm:<br />
<br />
<pre><br />
# Local condition to check that a custom field<br />
# has been set to "Required".<br />
# -Chuck Boeheim 3/13/06<br />
<br />
package RT::Condition::FieldRequired;<br />
require RT::Condition;<br />
<br />
use strict;<br />
use vars qw/@ISA/;<br />
@ISA = qw(RT::Condition);<br />
<br />
<br />
=head2 IsApplicable<br />
<br />
If the field named as an argument becomes 'Required'.<br />
Only triggers on transitions, not if it already had<br />
that value.<br />
<br />
=cut<br />
<br />
sub IsApplicable {<br />
my $self = shift;<br />
my $field = $self->Argument;<br />
my $trans = $self->TransactionObj;<br />
if ($trans->Type eq 'Create') {<br />
return 1 if $trans->TicketObj->FirstCustomFieldValue($field) =~ /^Required/;<br />
}<br />
if ($trans->Type eq 'CustomField') {<br />
my $cf = RT::CustomField->new($self->CurrentUser);<br />
$cf->Load($field);<br />
return 1 if $trans->Field == $cf->Id &amp;&amp; $trans->NewValue =~ /^Required/;<br />
}<br />
return undef;<br />
}<br />
<br />
1;<br />
</pre><br />
<br />
The new action can be installed with the following script, which needs to have your custom field names put in the appropriate place:<br />
<br />
<pre><br />
# To install, install FieldRequired.pm in local/lib/RT/Conditions, and<br />
# this script in local/etc/FieldRequired.install<br />
# /path/to/rt3/sbin/rt-setup-database --action insert \<br />
# --datafile /path/to/rt3/local/etc/FieldRequired.install<br />
#<br />
@ScripConditions = (<br />
{ Name => 'On Power Required',<br />
Description => 'Trigger when custom field Power becomes Required',<br />
ExecModule => 'FieldRequired',<br />
Argument => 'Power', # Your custom field name<br />
ApplicableTransTypes => 'Any' },<br />
{ Name => 'On DNS Required',<br />
Description => 'Trigger when custom field DNS becomes Required',<br />
ExecModule => 'FieldRequired',<br />
Argument => 'DNS', # Next custom field name<br />
ApplicableTransTypes => 'Any' },<br />
# Repeat for additional fields<br />
);<br />
</pre><br />
<br />
Then add scrips to the Installation queue for each subtask that use your new conditions:<br />
<br />
<pre><br />
Description: Create Power Ticket<br />
Condition: On Power Required <== The ScripCondition Name in the previous step<br />
Action: Create Tickets<br />
Template: PowerTicket <== The template name in the template step above<br />
Stage: TransactionCreate<br />
</pre> <br />
<br />
At this point, if you create a ticket in the Installation queue and set a custom field to 'Required' it will create the child ticket. It will also do this if you update an existing ticket to set a 'Required' status. This allows you to create a ticket for planning, and only initiate the subtasks at the time they are necessary.<br />
<br />
== Completing Subtasks ==<br />
<br />
Now one final piece of glue to update the parent status when the child completes. Create another scrip in the queue(s) that have the child tickets. (This is one reason to keep the child tickets in the installation queue.)<br />
<br />
<pre><br />
Description: On Resolve, Update Parent<br />
Condition: On Resolve<br />
Action: User Defined<br />
Template: Global Template Blank<br />
Stage: TransactionCreate<br />
</pre><br />
<br />
CustomCondition:<br />
None<br />
<br />
CustomActionPreparationCode:<br />
<pre><br />
return 1;<br />
</pre><br />
<br />
<br />
CustomActionCleanupCode:<br />
<pre><br />
return 1 if ($self->TransactionObj->NewValue !~ /^(?:resolved|deleted|rejected)$/);<br />
# Figure out which kind of child this is<br />
my $cf_value = 'Completed';<br />
my $cf_name;<br />
<br />
my $subject = $self->TicketObj->Subject;<br />
$cf_name = 'Power' if $subject =~ /^Power::/;<br />
$cf_name = 'Network' if $subject =~ /^Network::/;<br />
# Repeat for your custom field names.<br />
# There may be a better way to do this.<br />
<br />
# Alternative:<br />
# Setup the templates to use the same name as the custom fields, then you can<br />
# extract the name of each field from the subject, using Perl regex grouping.<br />
# my $subject = $self->TicketObj->Subject;<br />
# if($subject =~ /^(.*)::/)<br />
# {$cf_name = $1;}<br />
<br />
return undef unless $cf_name;<br />
<br />
my $actor = $self->TransactionObj->CreatorObj;<br />
my $actorname = $actor->RealName . ' (' . $actor->EmailAddress . ')';<br />
my $CF_Obj = RT::CustomField->new($self->CurrentUser);<br />
# current ticket is member of(child of some parents)<br />
my $MemberOf = $self->TicketObj->MemberOf;<br />
while (my $l = $MemberOf->Next ) {<br />
# we can't check non local objects<br />
next unless( $l->TargetURI->IsLocal );<br />
<br />
# Update the custom field in the parent ticket to show completed.<br />
$CF_Obj->LoadByName( Name => $cf_name,<br />
Queue => $l->TargetObj->QueueObj->Id);<br />
$CF_Obj->AddValueForObject( Object => $l->TargetObj,<br />
Content => $cf_value );<br />
my $id = $self->TicketObj->id;<br />
my $status = $self->TicketObj->Status;<br />
$l->TargetObj->Correspond(Content => <<'END');<br />
Child ticket completed:<br />
Ticket: $id<br />
Status: $status<br />
Subject: $subject<br />
By: $actorname<br />
END<br />
}<br />
<br />
return 1;<br />
</pre></div>Alexmvhttps://rt-wiki.bestpractical.com/index.php?title=PostgreSQLFullText&diff=2630PostgreSQLFullText2014-04-09T15:08:36Z<p>Alexmv: </p>
<hr />
<div>{{Outdated}}<br />
<br />
This article will describe, in short, what I have done to speed up queries which searches inside email bodies/attachments and RTFM articles which contain large amounts of [[WikiText]].<br />
<br />
There are 3 things that need to be done:<br />
<br />
* patch [[SearchBuilder]], to change LIKE 'search' to @@ plainto_tsquery('search')<br />
* add a column to hold the processed .content/.largecontent fields<br />
* add [[PostgreSQL]] Text indexes for Attachments.content and [[ObjectCustomFieldValues]].largecontent<br />
<br />
Attached are the file needed todo this, that functionality isn't there anymore ;-( So here they come inline<br />
<br />
This is the procedure that was followed to add full text<br />
<br />
search support to attachments and RTFM Largecontent fields.<br />
<br />
1. Patch [[SearchBuilder]].pm.<br />
<br />
2. Add a tsvector column to the attachements table to allow searching.<br />
<br />
<nowiki>ALTER TABLE attachments ADD COLUMN textsearchable tsvector;<br />
UPDATE attachments SET textsearchable =<br />
to_tsvector('english', coalesce(subject,'') || coalesce(content,''));<br />
<br />
This first command failed with the error:<br />
ERROR: string is too long for tsvector<br />
<br />
</nowiki><br />
<br />
So I am adding the tsvectors only to those entries with a size &lt; 500KB:<br />
<br />
<nowiki>UPDATE attachments SET textsearchable = to_tsvector('english',<br />
substring(coalesce(subject,'') || coalesce(content,''), 1, 500000));<br />
<br />
</nowiki><br />
<br />
Add the same text search column to objectcustomfieldvalues to index largecontent:<br />
<br />
<nowiki>ALTER TABLE objectcustomfieldvalues ADD COLUMN textsearchable tsvector;<br />
UPDATE objectcustomfieldvalues SET textsearchable = to_tsvector('english',<br />
substring(coalesce(largecontent,''), 1, 500000));<br />
<br />
</nowiki><br />
<br />
Now add an index on the new column to speed up searches. Note, this can be either a GIST or GIN index. GIN is faster to search but larger and slower to update while GIST is slower to search but the index is smaller and faster to update -- pick your poison:<br />
<br />
CREATE INDEX attachments_textsearch ON attachments<br />
USING gist(textsearchable);<br />
<br />
CREATE INDEX largecontent_textsearch ON objectcustomfieldvalues<br />
USING gist(textsearchable);<br />
<br />
Here are the index creation commands using the GIN index type. I have tried both, and unless you are in an extremely update intensive environment you will really want GIN -- very, very fast queries.<br />
<br />
CREATE INDEX attachments_textsearch ON attachments<br />
USING GIN (textsearchable );<br />
<br />
CREATE INDEX largecontent_textsearch ON objectcustomfieldvalues<br />
USING GIN (textsearchable );<br />
<br />
Here is the patch to [[DBIx]]::[[SearchBuilder]] to add the [[PostgreSQL]] support:<br />
<br />
<nowiki>DBIx&gt; diff -u SearchBuilder.pm_*<br />
--- SearchBuilder.pm_ORIG 2009-01-28 09:13:38.000000000 -0600<br />
+++ SearchBuilder.pm_FULLTEXT 2009-02-01 15:36:52.000000000 -0600<br />
@@ -926,6 +926,22 @@<br />
<br />
}<br />
<br />
+ # Use FULLTEXT for large attachments.content and<br />
+ # objectcustomfieldvalues.largecontent in PostgreSQL.<br />
+ if (($QualifiedField =~ /Attachments_\d+\.Content/) or<br />
+ ($QualifiedField =~ /ObjectCustomFieldValues_\d+\.Largecontent/)) {<br />
+ if (($args{'OPERATOR'} eq 'LIKE') or ($args{'OPERATOR'} eq 'ILIKE')) {<br />
+ $QualifiedField =~ s/(?:Content|Largecontent)/textsearchable/i;<br />
+ $args{'OPERATOR'} = '@@';<br />
+ $args{'VALUE'} = "plainto_tsquery($args{'VALUE'})";<br />
+ }<br />
+ if (($args{'OPERATOR'} eq 'NOT LIKE') or ($args{'OPERATOR'} eq 'NOT ILIKE')) {<br />
+ $QualifiedField =~ s/(?:Content|Largecontent)/textsearchable/i;<br />
+ $args{'OPERATOR'} = '@@';<br />
+ $args{'VALUE'} = "(!! plainto_tsquery($args{'VALUE'}))";<br />
+ }<br />
+ }<br />
+<br />
my $clause = {<br />
field =&gt; $QualifiedField,<br />
op =&gt; $args{'OPERATOR'},<br />
<br />
--------------------------------------------------------------------<br />
<br />
</nowiki><br />
<br />
A couple of comments about the approach used. I added a second column to hold the processed data. This is needed because there are certain conditions that cause FULL TEXT indexing to fail and it is easier to work around using a trigger to generate the tsvector column instead of having the index cause the INSERT to fail completely. In this case, it will not be indexed but these a pathological cases that really should not be searched anyway. The final piece is to setup a trigger to update the textsearchable column whenever the attachment.(subject/content) or objectcustomfieldvalues.largcontent are updated to keep the searching accurate.<br />
<br />
We also do not bother with stripping the '%' characters or the exit early tests in Handle.pm as the [[OracleText]] patches do. The reason is that the plainto_tsquery() will strip them for you so the basic patch is much simpler. Obviously, this search technique can be applied to any arbitrary field.<br />
<br />
Okay, here are the two triggers you need to keep the texsearchable columns updated when the attachments.subject/content or objectcustomfieldvalues.largecontent are changed:<br />
<br />
CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE<br />
ON attachments FOR EACH ROW EXECUTE PROCEDURE<br />
tsvector_update_trigger(textsearchable, 'pg_catalog.english', subject, content);<br />
<br />
CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE<br />
ON objectcustomfieldvalues FOR EACH ROW EXECUTE PROCEDURE<br />
tsvector_update_trigger(textsearchable, 'pg_catalog.english', largecontent);<br />
<br />
The set of triggers above will update the processed document column for every change. If you need more restricting updates, use something like the following which only processes the first 1/2MB of each attachment:<br />
<br />
<nowiki>CREATE FUNCTION attachments_trigger() RETURNS trigger AS $$<br />
begin<br />
new.textsearchable :=<br />
to_tsvector('pg_catalog.english', substring(coalesce(new.subject, '') || coalesce(new.content, '') from 1 for 500000));<br />
return new;<br />
end<br />
$$ LANGUAGE plpgsql;<br />
<br />
CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE<br />
ON attachments FOR EACH ROW EXECUTE PROCEDURE attachments_trigger();<br />
<br />
CREATE FUNCTION objectcustomfieldvalues_trigger() RETURNS trigger AS $$<br />
begin<br />
new.textsearchable :=<br />
to_tsvector('pg_catalog.english', substring(coalesce(new.largecontent, '') from 1 for 500000));<br />
return new;<br />
end<br />
$$ LANGUAGE plpgsql;<br />
<br />
CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE<br />
ON objectcustomfieldvalues FOR EACH ROW EXECUTE PROCEDURE<br />
objectcustomfieldvalues_trigger();<br />
<br />
</nowiki><br />
<br />
Please send me any comments or feedback. --Ken</div>Alexmvhttps://rt-wiki.bestpractical.com/index.php?title=CreateTicket&diff=657CreateTicket2012-05-15T17:27:43Z<p>Alexmv: </p>
<hr />
<div>A user with the '''CreateTicket''' right can create a ticket in a queue. RT also checks this right when you try to move a ticket into a queue (see info in [[ModifyTicket]]).<br />
<br />
This right can be granted to users and groups on a global level or on a queue-by-queue basis.<br />
<br />
This right should generally be granted to the Everybody group, so that even an unknown user can create a ticket by sending E-mail to the system.<br />
<br />
See also: [[Rights]], [[GlobalRights]]</div>Alexmvhttps://rt-wiki.bestpractical.com/index.php?title=CreateTicket&diff=656CreateTicket2012-05-15T17:27:24Z<p>Alexmv: Global CreateTicket isn't a default right, but rather just a really common one</p>
<hr />
<div>A user with the '''Create''''''Ticket''' right can create a ticket in a queue. RT also checks this right when you try to move a ticket into a queue (see info in [[ModifyTicket]]).<br />
<br />
This right can be granted to users and groups on a global level or on a queue-by-queue basis.<br />
<br />
This right should generally be granted to the Everybody group, so that even an unknown user can create a ticket by sending E-mail to the system.<br />
<br />
See also: [[Rights]], [[GlobalRights]]</div>Alexmvhttps://rt-wiki.bestpractical.com/index.php?title=Menus&diff=2175Menus2012-01-12T22:43:22Z<p>Alexmv: Fix links</p>
<hr />
<div>There are three types of menus in RT4: the main menu, the page menu, and the page widgets.<br />
<br />
==Modifying Menus==<br />
All of these menus can be altered, culled, and augmented by [[CustomizingWithCallbacks]]. The main RT menu can be modified from the "Menu" object in the callback. Here is an example of adding a new root menu item to RT:<br />
<br />
=== local/html/Callbacks/*/Elements/Tabs/Privileged ===<br />
<br />
<%init><br />
# Add a brand new root menu item<br />
my $bps = Menu()->child(<br />
'bps', # any unique identifier<br />
title => 'Corporate',<br />
path => '[http://bestpractical.com http://bestpractical.com]'<br />
);<br />
<br />
#Add a submenu item to this root menu item<br />
$bps->child(<br />
'wiki',<br />
title => 'Wiki',<br />
path => '[http://wiki.bestpractical.com http://wiki.bestpractical.com]',<br />
);<br />
<br />
#Retrieve the 'actions' page menu item<br />
if (my $actions = PageMenu->child('actions')) {<br />
$actions->child(<br />
'newitem',<br />
title => loc('New Action'), path => '/new/thing/here',<br />
)<br />
}<br />
<br />
# Remove the 'home' root menu item (don't try this at home!)<br />
Menu->delete('home');<br />
</%init></div>Alexmvhttps://rt-wiki.bestpractical.com/index.php?title=ReleaseEngineering&diff=3095ReleaseEngineering2011-06-13T23:03:59Z<p>Alexmv: /* Upload it */</p>
<hr />
<div>=== Create releng branch ===<br />
<br />
git checkout -b 3.8.5-releng 3.8-trunk<br />
<br />
=== If necessary, git cherry-pick -x sha1 to pull changes from trunk to branch ===<br />
<br />
=== Tag the release ===<br />
<br />
git tag -s -m 'release 3.8.5' rt-3.8.5<br />
<br />
=== Make the archive ===<br />
<br />
make snapshot<br />
<br />
This will build you a snapshot tarball, GPG sign it and give you SHA1 sums<br />
<br />
If you need an updated lib/RT/Generated.pm locally, you need to remove autom4te.cache and rerun configure<br />
<br />
=== Upload it ===<br />
<br />
scp -rvp rt-3.8.5.tar.gz download.bestpractical.com:/opt/web/hosted/download.bestpractical.com/html/pub/rt/devel/<br />
scp -rvp rt-3.8.5.tar.gz.sig download.bestpractical.com:/opt/web/hosted/download.bestpractical.com/html/pub/rt/devel/<br />
<br />
When making a release, files live in rt/release and you need to make sure to update the rt.tar.gz symlink<br />
<br />
As soon as you upload a new version into pub/rt/release/, it will be displayed on the corporate site's front page and RT product page.<br />
<br />
=== Cleanup and preserve history ===<br />
<br />
# For RT 3.8: # this guarantees that git can find tags made from releng branches<br />
<br />
git checkout 3.8-trunk<br />
git merge --no-commit --no-ff 3.8.5-releng<br />
git checkout HEAD -- configure.ac<br />
git commit<br />
Note in message that you undid the version bump from the branch<br />
<br />
# For stable RT releases after 3.8:<br />
<br />
git checkout 4.0-trunk<br />
git merge 4.0.1-releng</div>Alexmvhttps://rt-wiki.bestpractical.com/index.php?title=User:Alexmv/wikia.css&diff=4961User:Alexmv/wikia.css2010-11-30T18:26:33Z<p>Alexmv: </p>
<hr />
<div>#SPOTLIGHT_FOOTER { display: none }</div>Alexmv