]>
Commit | Line | Data |
---|---|---|
01e3b242 MKG |
1 | #!/usr/bin/perl |
2 | # BEGIN BPS TAGGED BLOCK {{{ | |
3 | # | |
4 | # COPYRIGHT: | |
5 | # | |
6 | # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC | |
7 | # <sales@bestpractical.com> | |
8 | # | |
9 | # (Except where explicitly superseded by other copyright notices) | |
10 | # | |
11 | # | |
12 | # LICENSE: | |
13 | # | |
14 | # This work is made available to you under the terms of Version 2 of | |
15 | # the GNU General Public License. A copy of that license should have | |
16 | # been provided with this software, but in any event can be snarfed | |
17 | # from www.gnu.org. | |
18 | # | |
19 | # This work is distributed in the hope that it will be useful, but | |
20 | # WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
22 | # General Public License for more details. | |
23 | # | |
24 | # You should have received a copy of the GNU General Public License | |
25 | # along with this program; if not, write to the Free Software | |
26 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
27 | # 02110-1301 or visit their web page on the internet at | |
28 | # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. | |
29 | # | |
30 | # | |
31 | # CONTRIBUTION SUBMISSION POLICY: | |
32 | # | |
33 | # (The following paragraph is not intended to limit the rights granted | |
34 | # to you to modify and distribute this software under the terms of | |
35 | # the GNU General Public License and is only of importance to you if | |
36 | # you choose to contribute your changes and enhancements to the | |
37 | # community by submitting them to Best Practical Solutions, LLC.) | |
38 | # | |
39 | # By intentionally submitting any modifications, corrections or | |
40 | # derivatives to this work, or any other work intended for use with | |
41 | # Request Tracker, to Best Practical Solutions, LLC, you confirm that | |
42 | # you are the copyright holder for those contributions and you grant | |
43 | # Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, | |
44 | # royalty-free, perpetual, license to use, copy, create derivative | |
45 | # works based on those contributions, and sublicense and distribute | |
46 | # those contributions and any derivatives thereof. | |
47 | # | |
48 | # END BPS TAGGED BLOCK }}} | |
49 | use strict; | |
50 | use warnings; | |
51 | ||
52 | use lib "local/lib"; | |
53 | use lib "lib"; | |
54 | ||
55 | use RT; | |
56 | RT::LoadConfig(); | |
57 | RT->Config->Set('LogToScreen' => 'debug'); | |
58 | RT::Init(); | |
59 | ||
60 | $| = 1; | |
61 | ||
62 | my $db_name = RT->Config->Get('DatabaseName'); | |
63 | my $db_type = RT->Config->Get('DatabaseType'); | |
64 | ||
65 | my $dbh = $RT::Handle->dbh; | |
66 | ||
67 | my $found_fm_tables; | |
68 | foreach my $name ( $RT::Handle->_TableNames ) { | |
69 | next unless $name =~ /^fm_/i; | |
70 | $found_fm_tables->{lc $name}++; | |
71 | } | |
72 | ||
73 | unless ( $found_fm_tables->{fm_topics} && $found_fm_tables->{fm_objecttopics} ) { | |
74 | warn "Couldn't find topics tables, it appears you have RTFM 2.0 or earlier."; | |
75 | warn "This script cannot yet upgrade RTFM versions which are that old"; | |
76 | exit; | |
77 | } | |
78 | ||
79 | { # port over Articles | |
80 | my @columns = qw(id Name Summary SortOrder Class Parent URI Creator Created LastUpdatedBy LastUpdated); | |
81 | copy_tables('FM_Articles','Articles',\@columns); | |
82 | ||
83 | } | |
84 | ||
85 | ||
86 | { # port over Classes | |
87 | my @columns = qw(id Name Description SortOrder Disabled Creator Created LastUpdatedBy LastUpdated); | |
88 | if ( grep lc($_) eq 'hotlist', $RT::Handle->Fields('FM_Classes') ) { | |
89 | push @columns, 'HotList'; | |
90 | } | |
91 | copy_tables('FM_Classes','Classes',\@columns); | |
92 | } | |
93 | ||
94 | { # port over Topics | |
95 | my @columns = qw(id Parent Name Description ObjectType ObjectId); | |
96 | copy_tables('FM_Topics','Topics',\@columns); | |
97 | } | |
98 | ||
99 | { # port over ObjectTopics | |
100 | my @columns = qw(id Topic ObjectType ObjectId); | |
101 | copy_tables('FM_ObjectTopics','ObjectTopics',\@columns); | |
102 | } | |
103 | ||
104 | sub copy_tables { | |
105 | my ($source, $dest, $columns) = @_; | |
106 | my $column_list = join(', ',@$columns); | |
107 | my $sql; | |
108 | # SQLite: http://www.sqlite.org/lang_insert.html | |
109 | if ( $db_type eq 'mysql' || $db_type eq 'SQLite' ) { | |
110 | $sql = "insert into $dest ($column_list) select $column_list from $source"; | |
111 | } | |
112 | # Oracle: http://www.adp-gmbh.ch/ora/sql/insert/select_and_subquery.html | |
113 | elsif ( $db_type eq 'Pg' || $db_type eq 'Oracle' ) { | |
114 | $sql = "insert into $dest ($column_list) (select $column_list from $source)"; | |
115 | } | |
116 | $RT::Logger->debug($sql); | |
117 | $dbh->do($sql); | |
118 | } | |
119 | ||
120 | { # create ObjectClasses | |
121 | # this logic will need updating when folks have an FM_ObjectClasses table | |
122 | use RT::Classes; | |
123 | use RT::ObjectClass; | |
124 | ||
125 | my $classes = RT::Classes->new(RT->SystemUser); | |
126 | $classes->UnLimit; | |
127 | while ( my $class = $classes->Next ) { | |
128 | my $objectclass = RT::ObjectClass->new(RT->SystemUser); | |
129 | my ($ret, $msg ) = $objectclass->Create( Class => $class->Id, ObjectType => 'RT::System', ObjectId => 0 ); | |
130 | if ($ret) { | |
131 | warn("Applied Class '".$class->Name."' globally"); | |
132 | } else { | |
133 | warn("Couldn't create linkage for Class ".$class->Name.": $msg"); | |
134 | } | |
135 | } | |
136 | } | |
137 | ||
138 | { # update ACLs | |
139 | use RT::ACL; | |
140 | my $acl = RT::ACL->new(RT->SystemUser); | |
141 | $acl->Limit( FIELD => 'ObjectType', VALUE => 'RT::FM::Class' ); | |
142 | $acl->Limit( FIELD => 'ObjectType', VALUE => 'RT::FM::System' ); | |
143 | while ( my $ace = $acl->Next ) { | |
144 | if ( $ace->__Value('ObjectType') eq 'RT::FM::Class' ) { | |
145 | my ($ret, $msg ) = $ace->__Set( Field => 'ObjectType', Value => 'RT::Class'); | |
146 | warn "Fixing ACL ".$ace->Id." to refer to RT::Class: $msg"; | |
147 | } elsif ( $ace->__Value('ObjectType') eq 'RT::FM::System' ) { | |
148 | my ($ret, $msg) = $ace->__Set(Field => 'ObjectType', Value => 'RT::System'); | |
149 | warn "Fixing ACL ".$ace->Id." to refer to RT::System: $msg"; | |
150 | } | |
151 | } | |
152 | ||
153 | ||
154 | } | |
155 | ||
156 | { # update CustomFields | |
157 | use RT::CustomFields; | |
158 | my $cfs = RT::CustomFields->new(RT->SystemUser); | |
159 | $cfs->Limit( FIELD => 'LookupType', VALUE => 'RT::FM::Class-RT::FM::Article' ); | |
160 | while ( my $cf = $cfs->Next ) { | |
161 | my ($ret, $msg) = $cf->__Set( Field => 'LookupType', Value => 'RT::Class-RT::Article' ); | |
162 | warn "Update Custom Field LookupType for CF.".$cf->Id." $msg"; | |
163 | } | |
164 | } | |
165 | ||
166 | { # update ObjectCustomFieldValues | |
167 | use RT::ObjectCustomFieldValues; | |
168 | my $ocfvs = RT::ObjectCustomFieldValues->new(RT->System); | |
169 | $ocfvs->Limit( FIELD => 'ObjectType', VALUE => 'RT::FM::Article' ); | |
170 | while ( my $ocfv = $ocfvs->Next ) { | |
171 | my ($ret, $msg) = $ocfv->__Set( Field => 'ObjectType', Value => 'RT::Article' ); | |
172 | warn "Updated CF ".$ocfv->__Value('CustomField')." Value for Article ".$ocfv->__Value('ObjectId'); | |
173 | } | |
174 | ||
175 | } | |
176 | ||
177 | { # update Topics | |
178 | use RT::Topics; | |
179 | my $topics = RT::Topics->new(RT->SystemUser); | |
180 | $topics->Limit( FIELD => 'ObjectType', VALUE => 'RT::FM::Class' ); | |
181 | $topics->Limit( FIELD => 'ObjectType', VALUE => 'RT::FM::System' ); | |
182 | while ( my $topic = $topics->Next ) { | |
183 | if ( $topic->__Value('ObjectType') eq 'RT::FM::Class' ) { | |
184 | my ($ret, $msg ) = $topic->__Set( Field => 'ObjectType', Value => 'RT::Class'); | |
185 | warn "Fixing Topic ".$topic->Id." to refer to RT::Class: $msg"; | |
186 | } elsif ( $topic->__Value('ObjectType') eq 'RT::FM::System' ) { | |
187 | my ($ret, $msg) = $topic->__Set(Field => 'ObjectType', Value => 'RT::System'); | |
188 | warn "Fixing Topic ".$topic->Id." to refer to RT::System: $msg"; | |
189 | } | |
190 | } | |
191 | } | |
192 | ||
193 | { # update ObjectTopics | |
194 | use RT::ObjectTopics; | |
195 | my $otopics = RT::ObjectTopics->new(RT->SystemUser); | |
196 | $otopics->UnLimit; | |
197 | while ( my $otopic = $otopics->Next ) { | |
198 | if ( $otopic->ObjectType eq 'RT::FM::Article' ) { | |
199 | my ($ret, $msg) = $otopic->SetObjectType('RT::Article'); | |
200 | warn "Fixing Topic ".$otopic->Topic." to apply to article: $msg"; | |
201 | } | |
202 | } | |
203 | } | |
204 | ||
205 | { # update Links | |
206 | use RT::Links; | |
207 | my $links = RT::Links->new(RT->SystemUser); | |
208 | $links->Limit(FIELD => 'Base', VALUE => 'rtfm', OPERATOR => 'LIKE', SUBCLAUSE => 'stopanding', ENTRYAGGREGATOR => 'OR'); | |
209 | $links->Limit(FIELD => 'Target', VALUE => 'rtfm', OPERATOR => 'LIKE', SUBCLAUSE => 'stopanding', ENTRYAGGREGATOR => 'OR' ); | |
210 | while ( my $link = $links->Next ) { | |
211 | my $base = $link->__Value('Base'); | |
212 | my $target = $link->__Value('Target'); | |
213 | if ( $base =~ s/rtfm/article/i ) { | |
214 | my ($ret, $msg) = $link->__Set( Field => 'Base', Value => $base ); | |
215 | warn "Updating base to $base: $msg for link ".$link->id; | |
216 | } | |
217 | if ( $target =~ s/rtfm/article/i ) { | |
218 | my ($ret, $msg) = $link->__Set( Field => 'Target', Value => $target ); | |
219 | warn "Updating target to $target: $msg for link ".$link->id; | |
220 | } | |
221 | ||
222 | } | |
223 | } | |
224 | ||
225 | { # update Transactions | |
226 | # we only keep article transactions at this point | |
227 | no warnings 'once'; | |
228 | use RT::Transactions; | |
229 | # Next calls Type to check readability and Type calls _Accessible | |
230 | # which called CurrentUserCanSee which calls Object which tries to instantiate | |
231 | # an RT::FM::Article. Rather than a shim RT::FM::Article class, I'm just avoiding | |
232 | # the ACL check since we're running around as the superuser. | |
233 | local *RT::Transaction::Type = sub { shift->__Value('Type') }; | |
234 | my $transactions = RT::Transactions->new(RT->SystemUser); | |
235 | $transactions->Limit( FIELD => 'ObjectType', VALUE => 'RT::FM::Article' ); | |
236 | while ( my $t = $transactions->Next ) { | |
237 | my ($ret, $msg) = $t->__Set( Field => 'ObjectType', Value => 'RT::Article' ); | |
238 | warn "Updated Transaction ".$t->Id." to point to RT::Article"; | |
239 | } | |
240 | ||
241 | # we also need to change links that point to articles | |
242 | $transactions = RT::Transactions->new(RT->SystemUser); | |
243 | $transactions->Limit( FIELD => 'Type', VALUE => 'AddLink' ); | |
244 | $transactions->Limit( FIELD => 'NewValue', VALUE => 'rtfm', OPERATOR => 'LIKE' ); | |
245 | while ( my $t = $transactions->Next ) { | |
246 | my $value = $t->__Value('NewValue'); | |
247 | $value =~ s/rtfm/article/; | |
248 | my ($ret, $msg) = $t->__Set( Field => 'NewValue', Value => $value ); | |
249 | warn "Updated Transaction ".$t->Id." to link to $value"; | |
250 | } | |
251 | } | |
252 | ||
253 | { # update Attributes | |
254 | # these are all things we should make real columns someday | |
255 | use RT::Attributes; | |
256 | my $attributes = RT::Attributes->new(RT->SystemUser); | |
257 | $attributes->Limit( FIELD => 'ObjectType', VALUE => 'RT::FM::Class' ); | |
258 | while ( my $a = $attributes->Next ) { | |
259 | my ($ret,$msg) = $a->__Set( Field => 'ObjectType', Value => 'RT::Class' ); | |
260 | warn "Updating Attribute ".$a->Name." to point to RT::Class"; | |
261 | } | |
262 | } |