1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC
6 # <sales@bestpractical.com>
8 # (Except where explicitly superseded by other copyright notices)
13 # This work is made available to you under the terms of Version 2 of
14 # the GNU General Public License. A copy of that license should have
15 # been provided with this software, but in any event can be snarfed
18 # This work is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 # General Public License for more details.
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 # 02110-1301 or visit their web page on the internet at
27 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
30 # CONTRIBUTION SUBMISSION POLICY:
32 # (The following paragraph is not intended to limit the rights granted
33 # to you to modify and distribute this software under the terms of
34 # the GNU General Public License and is only of importance to you if
35 # you choose to contribute your changes and enhancements to the
36 # community by submitting them to Best Practical Solutions, LLC.)
38 # By intentionally submitting any modifications, corrections or
39 # derivatives to this work, or any other work intended for use with
40 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 # you are the copyright holder for those contributions and you grant
42 # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
43 # royalty-free, perpetual, license to use, copy, create derivative
44 # works based on those contributions, and sublicense and distribute
45 # those contributions and any derivatives thereof.
47 # END BPS TAGGED BLOCK }}}
49 package RT::Migrate::Incremental;
58 'RT::Transaction' => sub {
60 $ref->{ObjectType} = 'RT::Ticket';
61 $ref->{ObjectId} = delete $ref->{Ticket};
62 delete $ref->{EffectiveTicket};
64 'RT::TicketCustomFieldValue' => sub {
65 my ($ref, $classref) = @_;
66 $$classref = "RT::ObjectCustomFieldValue";
67 $ref->{ObjectType} = 'RT::Ticket';
68 $ref->{ObjectId} = delete $ref->{Ticket};
70 '-RT::TicketCustomFieldValue' => sub {
71 my ($ref, $classref) = @_;
72 $$classref = "RT::ObjectCustomFieldValue";
74 'RT::CustomField' => sub {
76 $ref->{MaxValues} = 0 if $ref->{Type} =~ /Multiple$/;
77 $ref->{MaxValues} = 1 if $ref->{Type} =~ /Single$/;
78 $ref->{Type} = 'Select' if $ref->{Type} =~ /^Select/;
79 $ref->{Type} = 'Freeform' if $ref->{Type} =~ /^Freeform/;
80 $ref->{LookupType} = 'RT::Queue-RT::Ticket';
83 '+RT::CustomField' => sub {
86 "RT::ObjectCustomField" => rand(1),
89 CustomField => $ref->{id},
90 ObjectId => $ref->{Queue},
91 SortOrder => $ref->{SortOrder},
92 Creator => $ref->{Creator},
93 LastUpdatedBy => $ref->{LastUpdatedBy},
100 'RT::ObjectCustomFieldValue' => sub {
102 $ref->{Disabled} = not delete $ref->{Current};
109 return if defined $ref->{Description} and length $ref->{Description};
111 my $scrip = RT::Scrip->new( $RT::SystemUser );
112 $scrip->Load( $ref->{id} );
113 my $condition = $scrip->ConditionObj->Name
114 || $scrip->ConditionObj->Description
115 || ('On Condition #'. $scrip->Condition);
116 my $action = $scrip->ActionObj->Name
117 || $scrip->ActionObj->Description
118 || ('Run Action #'. $scrip->Action);
119 $ref->{Description} = join ' ', $condition, $action;
127 'RT::Template' => sub {
129 return unless $ref->{Queue};
131 my $queue = RT::Queue->new( $RT::SystemUser );
132 $queue->Load( $ref->{Queue} );
133 return unless $queue->Id and $queue->Name eq "___Approvals";
135 $ref->{Name} = "[OLD] ".$ref->{Name};
137 'RT::Attribute' => sub {
139 return unless $ref->{Name} eq "Dashboard";
142 Storable::thaw(MIME::Base64::decode_base64($ref->{Content}))
144 return unless $v and exists $v->{Searches};
148 my ($privacy, $id, $desc) = @$_;
150 portlet_type => 'search',
153 description => $desc,
156 } @{ delete $v->{Searches} }
159 $ref->{Content} = MIME::Base64::encode_base64(
160 Storable::nfreeze($v) );
163 my ($ref, $classref) = @_;
164 return unless $ref->{Queue};
166 my $queue = RT::Queue->new( $RT::SystemUser );
167 $queue->Load( $ref->{Queue} );
168 return unless $queue->Id and $queue->Name eq "___Approvals";
175 'RT::ScripAction' => sub {
177 return unless ($ref->{Argument}||"") eq "All";
178 if ($ref->{ExecModule} eq "Notify") {
179 $ref->{Name} = 'Notify Owner, Requestors, Ccs and AdminCcs';
180 $ref->{Description} = 'Send mail to owner and all watchers';
181 } elsif ($ref->{ExecModule} eq "NotifyAsComment") {
182 $ref->{Name} = 'Notify Owner, Requestors, Ccs and AdminCcs as Comment';
183 $ref->{Description} = 'Send mail to owner and all watchers as a "comment"';
189 'RT::ScripAction' => sub {
191 return unless $ref->{ExecModule} eq "NotifyGroup"
192 or $ref->{ExecModule} eq "NotifyGroupAsComment";
194 my $argument = $ref->{Argument};
195 if ( my $struct = eval { Storable::thaw( $argument ) } ) {
197 foreach my $r ( @{ $struct } ) {
199 next unless $r->{'Type'};
200 if( lc $r->{'Type'} eq 'user' ) {
201 $obj = RT::User->new( $RT::SystemUser );
202 } elsif ( lc $r->{'Type'} eq 'group' ) {
203 $obj = RT::Group->new( $RT::SystemUser );
207 $obj->Load( $r->{'Instance'} );
208 next unless $obj->id ;
212 $ref->{Argument} = join ",", @res;
214 $ref->{Argument} = join ",", grep length, split /[^0-9]+/, $argument;
220 'RT::ObjectCustomField' => sub {
221 # XXX Removing OCFs applied both global and non-global
222 # XXX Fixing SortOrder on OCFs
229 my $prefix = RT::URI::fsck_com_rt->LocalURIPrefix . '/ticket/';
230 for my $dir (qw(Target Base)) {
231 next unless $ref->{$dir} =~ /^$prefix(.*)/;
232 next unless int($1) eq $1;
233 next if $ref->{'Local'.$dir};
234 $ref->{'Local'.$dir} = $1;
237 'RT::Template' => sub {
240 return unless $ref->{Name} =~
241 /^(All Approvals Passed|Approval Passed|Approval Rejected)$/;
243 my $queue = RT::Queue->new( $RT::SystemUser );
244 $queue->Load( $ref->{Queue} );
245 return unless $queue->Id and $queue->Name eq "___Approvals";
248 s!(?<=Your ticket has been (?:approved|rejected) by { eval { )\$Approval->OwnerObj->Name!\$Approver->Name!;
253 'RT::Template' => sub {
255 $ref->{Type} = 'Perl';
257 # XXX: Add ExecuteCode to principals that currently have ModifyTemplate or ModifyScrips
262 my ($ref, $classref) = @_;
263 $$classref = undef if $ref->{DelegatedBy} > 0
264 or $ref->{DelegatedFrom} > 0;
267 'RT::GroupMember' => sub {
268 my ($ref, $classref) = @_;
269 my $group = RT::Group->new( $RT::SystemUser );
270 $group->Load( $ref->{GroupId} );
271 $$classref = undef if $group->Domain eq "Personal";
274 my ($ref, $classref) = @_;
275 $$classref = undef if $ref->{Domain} eq "Personal";
277 'RT::Principal' => sub {
278 my ($ref, $classref) = @_;
279 return unless $ref->{PrincipalType} eq "Group";
280 my $group = RT::Group->new( $RT::SystemUser );
281 $group->Load( $ref->{ObjectId} );
282 $$classref = undef if $group->Domain eq "Personal";
289 delete $ref->{DelegatedBy};
290 delete $ref->{DelegatedFrom};
295 'RT::CustomFieldValue' => sub {
297 my $attr = RT::Attribute->new( $RT::SystemUser );
299 ObjectType => "RT::CustomFieldValue",
300 ObjectId => $ref->{Id},
303 $ref->{Category} = $attr->Content if $attr->id;
305 'RT::Attribute' => sub {
306 my ($ref, $classref) = @_;
307 $$classref = undef if $ref->{Name} eq "Category"
308 and $ref->{ObjectType} eq "RT::CustomFieldValue";
315 my $attr = RT::Attribute->new( $RT::SystemUser );
317 ObjectType => "RT::User",
318 ObjectId => $ref->{id},
321 $ref->{AuthToken} = $attr->Content if $attr->id;
323 'RT::CustomField' => sub {
325 for my $name (qw/RenderType BasedOn ValuesClass/) {
326 my $attr = RT::Attribute->new( $RT::SystemUser );
328 ObjectType => "RT::CustomField",
329 ObjectId => $ref->{id},
332 $ref->{$name} = $attr->Content if $attr->id;
337 my $attr = RT::Attribute->new(
338 ObjectType => "RT::System",
340 Name => "BrandedSubjectTag",
342 return unless $attr->id;
343 my $map = $attr->Content || {};
344 return unless $map->{$ref->{id}};
345 $ref->{SubjectTag} = $map->{$ref->{id}};
347 'RT::Attribute' => sub {
348 my ($ref, $classref) = @_;
349 if ($ref->{ObjectType} eq "RT::User" and $ref->{Name} eq "AuthToken") {
351 } elsif ($ref->{ObjectType} eq "RT::CustomField" and $ref->{Name} eq "RenderType") {
353 } elsif ($ref->{ObjectType} eq "RT::CustomField" and $ref->{Name} eq "BasedOn") {
355 } elsif ($ref->{ObjectType} eq "RT::CustomField" and $ref->{Name} eq "ValuesClass") {
357 } elsif ($ref->{ObjectType} eq "RT::System" and $ref->{Name} eq "BrandedSubjectTag") {
364 # XXX RTFM => Articles
370 return unless $ref->{Name} eq '___Approvals';
371 $ref->{Lifecycle} = "approvals";
377 my ($ref, $classref) = @_;
378 my $group = RT::Group->new( $RT::SystemUser );
380 id => $ref->{PrincipalId},
381 Domain => "Personal",
383 $$classref = undef if $group->id;
384 $$classref = undef if $ref->{RightName} =~
385 /^(AdminOwnPersonalGroups|AdminAllPersonalGroups|DelegateRights)$/;
386 $$classref = undef if $ref->{RightName} =~
387 /^(RejectTicket|ModifyTicketStatus)$/;
392 'RT::Template' => sub {
394 $ref->{Type} ||= 'Perl';
399 'RT::Transaction' => sub {
401 return unless $ref->{ObjectType} eq "RT::User" and $ref->{Field} eq "Password";
402 $ref->{OldValue} = $ref->{NewValue} = '********';
409 $ref->{Lifecycle} ||= 'default';
416 'RT::Attribute' => sub {
418 return unless $ref->{Name} eq "HomepageSettings";
421 Storable::thaw(MIME::Base64::decode_base64($ref->{Content}))
423 return if not $v or $v->{sidebar};
424 $v->{sidebar} = delete $v->{summary};
425 $ref->{Content} = MIME::Base64::encode_base64(
426 Storable::nfreeze($v) );
431 '+RT::Scrip' => sub {
434 "RT::ObjectScrip" => rand(1),
438 Stage => delete $ref->{Stage},
439 ObjectId => delete $ref->{Queue},
440 Creator => $ref->{Creator},
441 Created => $ref->{Created},
442 LastUpdatedBy => $ref->{LastUpdatedBy},
443 LastUpdated => $ref->{LastUpdated},
446 if ( $new->[2]{Stage} eq "Disabled" ) {
447 $ref->{Disabled} = 1;
448 $new->[2]{Stage} = "TransactionCreate";
450 $ref->{Disabled} = 0;
461 if $ref->{Domain} eq "RT::System-Role"
462 and $ref->{Instance} = 0;
470 my $template = RT::Template->new( $RT::SystemUser );
471 $template->Load( $ref->{Template} );
472 $ref->{Template} = $template->id ? $template->Name : 'Blank';
477 'RT::Attribute' => sub {
479 return unless $ref->{Name} eq RT::User::_PrefName( RT->System )
480 and $ref->{ObjectType} eq "RT::User";
482 Storable::thaw(MIME::Base64::decode_base64($ref->{Content}))
484 return if not $v or $v->{ShowHistory};
485 $v->{ShowHistory} = delete $v->{DeferTransactionLoading}
487 $ref->{Content} = MIME::Base64::encode_base64(
488 Storable::nfreeze($v) );
493 'RT::Transaction' => sub {
495 return unless $ref->{ObjectType} eq 'RT::Ticket'
496 and $ref->{Type} eq 'Set'
497 and $ref->{Field} eq 'TimeWorked';
498 $ref->{TimeTaken} = $ref->{NewValue} - $ref->{OldValue};
503 'RT::Ticket' => sub {
505 $ref->{IsMerged} = 1 if $ref->{id} != $ref->{EffectiveId};
510 'RT::ObjectcustomFieldValue' => sub {
512 $ref->{Content} = undef if defined $ref->{LargeContent}
513 and defined $ref->{Content} and $ref->{Content} eq '';
518 'RT::CustomField' => sub {
520 delete $ref->{Repeated};
527 $ref->{Name} = $ref->{Type}
528 if $ref->Domain =~ /^(ACLEquivalence|SystemInternal|.*-Role)$/;
535 delete $ref->{ConditionRules};
536 delete $ref->{ActionRules};
541 'RT::Attribute' => sub {
543 return unless $ref->{Name} eq 'SavedSearch';
545 Storable::thaw(MIME::Base64::decode_base64($ref->{Content}))
547 return unless $v and ref $v and ($v->{SearchType}||'') eq 'Chart';
549 # Switch from PrimaryGroupBy to GroupBy name
550 # Switch from "CreatedMonthly" to "Created.Monthly"
551 $v->{GroupBy} ||= [delete $v->{PrimaryGroupBy}];
552 for (@{$v->{GroupBy}}) {
554 s/(?<=[a-z])(?=[A-Z])/./;
556 $ref->{Content} = MIME::Base64::encode_base64(
557 Storable::nfreeze($v) );
562 'RT::Template' => sub {
564 delete $ref->{Language};
565 delete $ref->{TranslationOf};
570 'RT::Template' => sub {
572 if ($ref->{Name} eq 'Forward') {
573 $ref->{Description} = 'Forwarded message';
574 if ( $ref->{Content} =~
575 m/^\n*This is (a )?forward of transaction #\{\s*\$Transaction->id\s*\} of (a )?ticket #\{\s*\$Ticket->id\s*\}\n*$/
578 { $ForwardTransaction->Content =~ /\S/ ? $ForwardTransaction->Content : "This is a forward of transaction #".$Transaction->id." of ticket #". $Ticket->id }
581 RT->Logger->error('Current "Forward" template is not the default version, please check docs/4.2-UPGRADING');
583 } elsif ($ref->{Name} eq 'Forward Ticket') {
584 $ref->{Description} = 'Forwarded ticket message';
585 if ( $ref->{Content} eq q{
587 This is a forward of ticket #{ $Ticket->id }
590 { $ForwardTransaction->Content =~ /\S/ ? $ForwardTransaction->Content : "This is a forward of ticket #". $Ticket->id }
593 RT->Logger->error('Current "Forward Ticket" template is not the default version, please check docs/4.2-UPGRADING');
600 # XXX User dashboards
604 'RT::Template' => sub {
606 return unless $ref->{Name} eq 'Error: bad GnuPG data';
607 $ref->{Name} = 'Error: bad encrypted data';
608 $ref->{Description} =
609 'Inform user that a message he sent has invalid encryption data';
610 $ref->{Content} =~ s/GnuPG signature/signature/g;
613 'RT::Attribute' => sub {
614 my ($ref, $classref) = @_;
615 if ($ref->{ObjectType} eq "RT::User" and $ref->{Name} eq "SMIMEKeyNotAfter") {