Master to 4.2.8
[usit-rt.git] / lib / RT / Migrate / Incremental.pm
CommitLineData
af59614d
MKG
1# BEGIN BPS TAGGED BLOCK {{{
2#
3# COPYRIGHT:
4#
320f0092 5# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
af59614d
MKG
6# <sales@bestpractical.com>
7#
8# (Except where explicitly superseded by other copyright notices)
9#
10#
11# LICENSE:
12#
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
16# from www.gnu.org.
17#
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.
22#
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.
28#
29#
30# CONTRIBUTION SUBMISSION POLICY:
31#
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.)
37#
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.
46#
47# END BPS TAGGED BLOCK }}}
48
49package RT::Migrate::Incremental;
50
51use strict;
52use warnings;
53require Storable;
54require MIME::Base64;
55
56our %UPGRADES = (
57 '3.3.0' => {
58 'RT::Transaction' => sub {
59 my ($ref) = @_;
60 $ref->{ObjectType} = 'RT::Ticket';
61 $ref->{ObjectId} = delete $ref->{Ticket};
62 delete $ref->{EffectiveTicket};
63 },
64 'RT::TicketCustomFieldValue' => sub {
65 my ($ref, $classref) = @_;
66 $$classref = "RT::ObjectCustomFieldValue";
67 $ref->{ObjectType} = 'RT::Ticket';
68 $ref->{ObjectId} = delete $ref->{Ticket};
69 },
70 '-RT::TicketCustomFieldValue' => sub {
71 my ($ref, $classref) = @_;
72 $$classref = "RT::ObjectCustomFieldValue";
73 },
74 'RT::CustomField' => sub {
75 my ($ref) = @_;
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';
81 delete $ref->{Queue};
82 },
83 '+RT::CustomField' => sub {
84 my ($ref) = @_;
85 return [
86 "RT::ObjectCustomField" => rand(1),
87 {
88 id => undef,
89 CustomField => $ref->{id},
90 ObjectId => $ref->{Queue},
91 SortOrder => $ref->{SortOrder},
92 Creator => $ref->{Creator},
93 LastUpdatedBy => $ref->{LastUpdatedBy},
94 }
95 ];
96 }
97 },
98
99 '3.3.11' => {
100 'RT::ObjectCustomFieldValue' => sub {
101 my ($ref) = @_;
102 $ref->{Disabled} = not delete $ref->{Current};
103 },
104 },
105
106 '3.7.19' => {
107 'RT::Scrip' => sub {
108 my ($ref) = @_;
109 return if defined $ref->{Description} and length $ref->{Description};
110
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;
120 },
121 },
122
123 # XXX BrandedQueues
124 # XXX iCal
125
126 '3.8.2' => {
127 'RT::Template' => sub {
128 my ($ref) = @_;
129 return unless $ref->{Queue};
130
131 my $queue = RT::Queue->new( $RT::SystemUser );
132 $queue->Load( $ref->{Queue} );
133 return unless $queue->Id and $queue->Name eq "___Approvals";
134
135 $ref->{Name} = "[OLD] ".$ref->{Name};
136 },
137 'RT::Attribute' => sub {
138 my ($ref) = @_;
139 return unless $ref->{Name} eq "Dashboard";
140
141 my $v = eval {
142 Storable::thaw(MIME::Base64::decode_base64($ref->{Content}))
143 };
144 return unless $v and exists $v->{Searches};
145 $v->{Panes} = {
146 body => [
147 map {
148 my ($privacy, $id, $desc) = @$_;
149 +{
150 portlet_type => 'search',
151 privacy => $privacy,
152 id => $id,
153 description => $desc,
154 pane => 'body',
155 }
156 } @{ delete $v->{Searches} }
157 ],
158 };
159 $ref->{Content} = MIME::Base64::encode_base64(
160 Storable::nfreeze($v) );
161 },
162 'RT::Scrip' => sub {
163 my ($ref, $classref) = @_;
164 return unless $ref->{Queue};
165
166 my $queue = RT::Queue->new( $RT::SystemUser );
167 $queue->Load( $ref->{Queue} );
168 return unless $queue->Id and $queue->Name eq "___Approvals";
169
170 $$classref = undef;
171 },
172 },
173
174 '3.8.3' => {
175 'RT::ScripAction' => sub {
176 my ($ref) = @_;
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"';
184 }
185 },
186 },
187
188 '3.8.4' => {
189 'RT::ScripAction' => sub {
190 my ($ref) = @_;
191 return unless $ref->{ExecModule} eq "NotifyGroup"
192 or $ref->{ExecModule} eq "NotifyGroupAsComment";
193
194 my $argument = $ref->{Argument};
195 if ( my $struct = eval { Storable::thaw( $argument ) } ) {
196 my @res;
197 foreach my $r ( @{ $struct } ) {
198 my $obj;
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 );
204 } else {
205 next;
206 }
207 $obj->Load( $r->{'Instance'} );
208 next unless $obj->id ;
209
210 push @res, $obj->id;
211 }
212 $ref->{Argument} = join ",", @res;
213 } else {
214 $ref->{Argument} = join ",", grep length, split /[^0-9]+/, $argument;
215 }
216 },
217 },
218
219 '3.8.8' => {
220 'RT::ObjectCustomField' => sub {
221 # XXX Removing OCFs applied both global and non-global
222 # XXX Fixing SortOrder on OCFs
223 },
224 },
225
226 '3.8.9' => {
227 'RT::Link' => sub {
228 my ($ref) = @_;
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;
235 }
236 },
237 'RT::Template' => sub {
238 my ($ref) = @_;
239
240 return unless $ref->{Name} =~
241 /^(All Approvals Passed|Approval Passed|Approval Rejected)$/;
242
243 my $queue = RT::Queue->new( $RT::SystemUser );
244 $queue->Load( $ref->{Queue} );
245 return unless $queue->Id and $queue->Name eq "___Approvals";
246
247 $ref->{Content} =~
248s!(?<=Your ticket has been (?:approved|rejected) by { eval { )\$Approval->OwnerObj->Name!\$Approver->Name!;
249 },
250 },
251
252 '3.9.1' => {
253 'RT::Template' => sub {
254 my ($ref) = @_;
255 $ref->{Type} = 'Perl';
256 },
257 # XXX: Add ExecuteCode to principals that currently have ModifyTemplate or ModifyScrips
258 },
259
260 '3.9.2' => {
261 'RT::ACE' => sub {
262 my ($ref, $classref) = @_;
263 $$classref = undef if $ref->{DelegatedBy} > 0
264 or $ref->{DelegatedFrom} > 0;
265 },
266
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";
272 },
273 'RT::Group' => sub {
274 my ($ref, $classref) = @_;
275 $$classref = undef if $ref->{Domain} eq "Personal";
276 },
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";
283 },
284 },
285
286 '3.9.3' => {
287 'RT::ACE' => sub {
288 my ($ref) = @_;
289 delete $ref->{DelegatedBy};
290 delete $ref->{DelegatedFrom};
291 },
292 },
293
294 '3.9.5' => {
295 'RT::CustomFieldValue' => sub {
296 my ($ref) = @_;
297 my $attr = RT::Attribute->new( $RT::SystemUser );
298 $attr->LoadByCols(
299 ObjectType => "RT::CustomFieldValue",
300 ObjectId => $ref->{Id},
301 Name => "Category",
302 );
303 $ref->{Category} = $attr->Content if $attr->id;
304 },
305 'RT::Attribute' => sub {
306 my ($ref, $classref) = @_;
307 $$classref = undef if $ref->{Name} eq "Category"
308 and $ref->{ObjectType} eq "RT::CustomFieldValue";
309 },
310 },
311
312 '3.9.7' => {
313 'RT::User' => sub {
314 my ($ref) = @_;
315 my $attr = RT::Attribute->new( $RT::SystemUser );
316 $attr->LoadByCols(
317 ObjectType => "RT::User",
318 ObjectId => $ref->{id},
319 Name => "AuthToken",
320 );
321 $ref->{AuthToken} = $attr->Content if $attr->id;
322 },
323 'RT::CustomField' => sub {
324 my ($ref) = @_;
325 for my $name (qw/RenderType BasedOn ValuesClass/) {
326 my $attr = RT::Attribute->new( $RT::SystemUser );
327 $attr->LoadByCols(
328 ObjectType => "RT::CustomField",
329 ObjectId => $ref->{id},
330 Name => $name,
331 );
332 $ref->{$name} = $attr->Content if $attr->id;
333 }
334 },
335 'RT::Queue' => sub {
336 my ($ref) = @_;
337 my $attr = RT::Attribute->new(
338 ObjectType => "RT::System",
339 ObjectId => 1,
340 Name => "BrandedSubjectTag",
341 );;
342 return unless $attr->id;
343 my $map = $attr->Content || {};
344 return unless $map->{$ref->{id}};
345 $ref->{SubjectTag} = $map->{$ref->{id}};
346 },
347 'RT::Attribute' => sub {
348 my ($ref, $classref) = @_;
349 if ($ref->{ObjectType} eq "RT::User" and $ref->{Name} eq "AuthToken") {
350 $$classref = undef;
351 } elsif ($ref->{ObjectType} eq "RT::CustomField" and $ref->{Name} eq "RenderType") {
352 $$classref = undef;
353 } elsif ($ref->{ObjectType} eq "RT::CustomField" and $ref->{Name} eq "BasedOn") {
354 $$classref = undef;
355 } elsif ($ref->{ObjectType} eq "RT::CustomField" and $ref->{Name} eq "ValuesClass") {
356 $$classref = undef;
357 } elsif ($ref->{ObjectType} eq "RT::System" and $ref->{Name} eq "BrandedSubjectTag") {
358 $$classref = undef;
359 }
360 },
361 },
362
363 '3.9.8' => {
364 # XXX RTFM => Articles
365 },
366
367 '4.0.0rc7' => {
368 'RT::Queue' => sub {
369 my ($ref) = @_;
370 return unless $ref->{Name} eq '___Approvals';
371 $ref->{Lifecycle} = "approvals";
372 },
373 },
374
375 '4.0.1' => {
376 'RT::ACE' => sub {
377 my ($ref, $classref) = @_;
378 my $group = RT::Group->new( $RT::SystemUser );
379 $group->LoadByCols(
380 id => $ref->{PrincipalId},
381 Domain => "Personal",
382 );
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)$/;
388 },
389 },
390
391 '4.0.4' => {
392 'RT::Template' => sub {
393 my ($ref) = @_;
394 $ref->{Type} ||= 'Perl';
395 },
396 },
397
398 '4.0.6' => {
399 'RT::Transaction' => sub {
400 my ($ref) = @_;
401 return unless $ref->{ObjectType} eq "RT::User" and $ref->{Field} eq "Password";
402 $ref->{OldValue} = $ref->{NewValue} = '********';
403 },
404 },
405
406 '4.0.9' => {
407 'RT::Queue' => sub {
408 my ($ref) = @_;
409 $ref->{Lifecycle} ||= 'default';
410 },
411 },
412
320f0092
MKG
413 '4.0.19' => {
414 'RT::CustomField' => sub {
415 my ($ref) = @_;
416 $ref->{LookupType} = 'RT::Class-RT::Article'
417 if $ref->{LookupType} eq 'RT::FM::Class-RT::FM::Article';
418 },
419 'RT::ObjectCustomFieldValue' => sub {
420 my ($ref) = @_;
421 $ref->{ObjectType} = 'RT::Article'
422 if $ref->{ObjectType} eq 'RT::FM::Article';
423 },
424 },
af59614d
MKG
425
426
427 '4.1.0' => {
428 'RT::Attribute' => sub {
429 my ($ref) = @_;
430 return unless $ref->{Name} eq "HomepageSettings";
431
432 my $v = eval {
433 Storable::thaw(MIME::Base64::decode_base64($ref->{Content}))
434 };
435 return if not $v or $v->{sidebar};
436 $v->{sidebar} = delete $v->{summary};
437 $ref->{Content} = MIME::Base64::encode_base64(
438 Storable::nfreeze($v) );
439 },
440 },
441
442 '4.1.1' => {
443 '+RT::Scrip' => sub {
444 my ($ref) = @_;
445 my $new = [
446 "RT::ObjectScrip" => rand(1),
447 {
448 id => undef,
449 Scrip => $ref->{id},
450 Stage => delete $ref->{Stage},
451 ObjectId => delete $ref->{Queue},
452 Creator => $ref->{Creator},
453 Created => $ref->{Created},
454 LastUpdatedBy => $ref->{LastUpdatedBy},
455 LastUpdated => $ref->{LastUpdated},
456 }
457 ];
458 if ( $new->[2]{Stage} eq "Disabled" ) {
459 $ref->{Disabled} = 1;
460 $new->[2]{Stage} = "TransactionCreate";
461 } else {
462 $ref->{Disabled} = 0;
463 }
464 # XXX SortOrder
465 return $new;
466 },
467 },
468
469 '4.1.4' => {
470 'RT::Group' => sub {
471 my ($ref) = @_;
472 $ref->{Instance} = 1
473 if $ref->{Domain} eq "RT::System-Role"
474 and $ref->{Instance} = 0;
475 },
476 # XXX Invalid rights
477 },
478
479 '4.1.5' => {
480 'RT::Scrip' => sub {
481 my ($ref) = @_;
482 my $template = RT::Template->new( $RT::SystemUser );
483 $template->Load( $ref->{Template} );
484 $ref->{Template} = $template->id ? $template->Name : 'Blank';
485 },
486 },
487
488 '4.1.6' => {
489 'RT::Attribute' => sub {
490 my ($ref) = @_;
491 return unless $ref->{Name} eq RT::User::_PrefName( RT->System )
492 and $ref->{ObjectType} eq "RT::User";
493 my $v = eval {
494 Storable::thaw(MIME::Base64::decode_base64($ref->{Content}))
495 };
496 return if not $v or $v->{ShowHistory};
497 $v->{ShowHistory} = delete $v->{DeferTransactionLoading}
498 ? "click" : "delay";
499 $ref->{Content} = MIME::Base64::encode_base64(
500 Storable::nfreeze($v) );
501 },
502 },
503
504 '4.1.7' => {
505 'RT::Transaction' => sub {
506 my ($ref) = @_;
507 return unless $ref->{ObjectType} eq 'RT::Ticket'
508 and $ref->{Type} eq 'Set'
509 and $ref->{Field} eq 'TimeWorked';
510 $ref->{TimeTaken} = $ref->{NewValue} - $ref->{OldValue};
511 },
512 },
513
514 '4.1.8' => {
515 'RT::Ticket' => sub {
516 my ($ref) = @_;
517 $ref->{IsMerged} = 1 if $ref->{id} != $ref->{EffectiveId};
518 },
519 },
520
521 '4.1.10' => {
522 'RT::ObjectcustomFieldValue' => sub {
523 my ($ref) = @_;
524 $ref->{Content} = undef if defined $ref->{LargeContent}
525 and defined $ref->{Content} and $ref->{Content} eq '';
526 },
527 },
528
529 '4.1.11' => {
530 'RT::CustomField' => sub {
531 my ($ref) = @_;
532 delete $ref->{Repeated};
533 },
534 },
535
536 '4.1.13' => {
537 'RT::Group' => sub {
538 my ($ref) = @_;
539 $ref->{Name} = $ref->{Type}
320f0092 540 if $ref->{Domain} =~ /^(ACLEquivalence|SystemInternal|.*-Role)$/;
af59614d
MKG
541 },
542 },
543
544 '4.1.14' => {
545 'RT::Scrip' => sub {
546 my ($ref) = @_;
547 delete $ref->{ConditionRules};
548 delete $ref->{ActionRules};
549 },
550 },
551
552 '4.1.17' => {
553 'RT::Attribute' => sub {
554 my ($ref) = @_;
555 return unless $ref->{Name} eq 'SavedSearch';
556 my $v = eval {
557 Storable::thaw(MIME::Base64::decode_base64($ref->{Content}))
558 };
559 return unless $v and ref $v and ($v->{SearchType}||'') eq 'Chart';
560
561 # Switch from PrimaryGroupBy to GroupBy name
562 # Switch from "CreatedMonthly" to "Created.Monthly"
563 $v->{GroupBy} ||= [delete $v->{PrimaryGroupBy}];
564 for (@{$v->{GroupBy}}) {
565 next if /\./;
566 s/(?<=[a-z])(?=[A-Z])/./;
567 }
568 $ref->{Content} = MIME::Base64::encode_base64(
569 Storable::nfreeze($v) );
570 },
571 },
572
573 '4.1.19' => {
574 'RT::Template' => sub {
575 my ($ref) = @_;
576 delete $ref->{Language};
577 delete $ref->{TranslationOf};
578 },
579 },
580
581 '4.1.20' => {
582 'RT::Template' => sub {
583 my ($ref) = @_;
584 if ($ref->{Name} eq 'Forward') {
585 $ref->{Description} = 'Forwarded message';
586 if ( $ref->{Content} =~
587 m/^\n*This is (a )?forward of transaction #\{\s*\$Transaction->id\s*\} of (a )?ticket #\{\s*\$Ticket->id\s*\}\n*$/
588 ) {
589 $ref->{Content} = q{
590{ $ForwardTransaction->Content =~ /\S/ ? $ForwardTransaction->Content : "This is a forward of transaction #".$Transaction->id." of ticket #". $Ticket->id }
591};
592 } else {
c33a4027 593 RT->Logger->error('Current "Forward" template is not the default version, please check docs/UPGRADING-4.2');
af59614d
MKG
594 }
595 } elsif ($ref->{Name} eq 'Forward Ticket') {
596 $ref->{Description} = 'Forwarded ticket message';
597 if ( $ref->{Content} eq q{
598
599This is a forward of ticket #{ $Ticket->id }
600} ) {
601 $ref->{Content} = q{
602{ $ForwardTransaction->Content =~ /\S/ ? $ForwardTransaction->Content : "This is a forward of ticket #". $Ticket->id }
603};
604 } else {
c33a4027 605 RT->Logger->error('Current "Forward Ticket" template is not the default version, please check docs/UPGRADING-4.2');
af59614d
MKG
606 }
607 }
608 },
609 },
610
611 '4.1.21' => {
612 # XXX User dashboards
613 },
614
615 '4.1.22' => {
616 'RT::Template' => sub {
617 my ($ref) = @_;
618 return unless $ref->{Name} eq 'Error: bad GnuPG data';
619 $ref->{Name} = 'Error: bad encrypted data';
620 $ref->{Description} =
621 'Inform user that a message he sent has invalid encryption data';
622 $ref->{Content} =~ s/GnuPG signature/signature/g;
623 },
624 # XXX SMIME keys
625 'RT::Attribute' => sub {
626 my ($ref, $classref) = @_;
627 if ($ref->{ObjectType} eq "RT::User" and $ref->{Name} eq "SMIMEKeyNotAfter") {
628 $$classref = undef;
629 }
630 },
631 },
320f0092
MKG
632
633 '4.2.1' => {
634 'RT::Attribute' => sub {
635 my ($ref, $classref) = @_;
636 if ($ref->{ObjectType} eq "RT::System" and $ref->{Name} eq "BrandedSubjectTag") {
637 $$classref = undef;
638 }
639 },
640 },
641
642 '4.2.2' => {
643 'RT::CustomField' => sub {
644 my ($ref) = @_;
645 $ref->{LookupType} = 'RT::Class-RT::Article'
646 if $ref->{LookupType} eq 'RT::FM::Class-RT::FM::Article';
647 },
648 'RT::ObjectCustomFieldValue' => sub {
649 my ($ref) = @_;
650 $ref->{ObjectType} = 'RT::Article'
651 if $ref->{ObjectType} eq 'RT::FM::Article';
652 },
653 },
654
af59614d
MKG
655);
656
6571;