]>
Commit | Line | Data |
---|---|---|
1 | # BEGIN BPS TAGGED BLOCK {{{ | |
2 | # | |
3 | # COPYRIGHT: | |
4 | # | |
5 | # This software is Copyright (c) 1996-2013 Best Practical Solutions, LLC | |
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 | ||
49 | package RT::ObjectCustomField; | |
50 | ||
51 | use strict; | |
52 | use warnings; | |
53 | ||
54 | ||
55 | use RT::CustomField; | |
56 | use base 'RT::Record'; | |
57 | ||
58 | sub Table {'ObjectCustomFields'} | |
59 | ||
60 | ||
61 | ||
62 | ||
63 | ||
64 | ||
65 | sub Create { | |
66 | my $self = shift; | |
67 | my %args = ( | |
68 | CustomField => 0, | |
69 | ObjectId => 0, | |
70 | SortOrder => undef, | |
71 | @_ | |
72 | ); | |
73 | ||
74 | my $cf = $self->CustomFieldObj( $args{'CustomField'} ); | |
75 | unless ( $cf->id ) { | |
76 | $RT::Logger->error("Couldn't load '$args{'CustomField'}' custom field"); | |
77 | return 0; | |
78 | } | |
79 | ||
80 | #XXX: Where is ACL check for 'AssignCustomFields'? | |
81 | ||
82 | my $ObjectCFs = RT::ObjectCustomFields->new($self->CurrentUser); | |
83 | $ObjectCFs->LimitToObjectId( $args{'ObjectId'} ); | |
84 | $ObjectCFs->LimitToCustomField( $cf->id ); | |
85 | $ObjectCFs->LimitToLookupType( $cf->LookupType ); | |
86 | if ( my $first = $ObjectCFs->First ) { | |
87 | $self->Load( $first->id ); | |
88 | return $first->id; | |
89 | } | |
90 | ||
91 | unless ( defined $args{'SortOrder'} ) { | |
92 | my $ObjectCFs = RT::ObjectCustomFields->new( RT->SystemUser ); | |
93 | $ObjectCFs->LimitToObjectId( $args{'ObjectId'} ); | |
94 | $ObjectCFs->LimitToObjectId( 0 ) if $args{'ObjectId'}; | |
95 | $ObjectCFs->LimitToLookupType( $cf->LookupType ); | |
96 | $ObjectCFs->OrderBy( FIELD => 'SortOrder', ORDER => 'DESC' ); | |
97 | if ( my $first = $ObjectCFs->First ) { | |
98 | $args{'SortOrder'} = $first->SortOrder + 1; | |
99 | } else { | |
100 | $args{'SortOrder'} = 0; | |
101 | } | |
102 | } | |
103 | ||
104 | return $self->SUPER::Create( | |
105 | CustomField => $args{'CustomField'}, | |
106 | ObjectId => $args{'ObjectId'}, | |
107 | SortOrder => $args{'SortOrder'}, | |
108 | ); | |
109 | } | |
110 | ||
111 | sub Delete { | |
112 | my $self = shift; | |
113 | ||
114 | my $ObjectCFs = RT::ObjectCustomFields->new($self->CurrentUser); | |
115 | $ObjectCFs->LimitToObjectId($self->ObjectId); | |
116 | $ObjectCFs->LimitToLookupType($self->CustomFieldObj->LookupType); | |
117 | ||
118 | # Move everything below us up | |
119 | my $sort_order = $self->SortOrder; | |
120 | while (my $OCF = $ObjectCFs->Next) { | |
121 | my $this_order = $OCF->SortOrder; | |
122 | next if $this_order <= $sort_order; | |
123 | $OCF->SetSortOrder($this_order - 1); | |
124 | } | |
125 | ||
126 | $self->SUPER::Delete; | |
127 | } | |
128 | ||
129 | ||
130 | =head2 CustomFieldObj | |
131 | ||
132 | Returns the CustomField Object which has the id returned by CustomField | |
133 | ||
134 | ||
135 | =cut | |
136 | ||
137 | sub CustomFieldObj { | |
138 | my $self = shift; | |
139 | my $id = shift || $self->CustomField; | |
140 | ||
141 | # To find out the proper context object to load the CF with, we need | |
142 | # data from the CF -- namely, the record class. Go find that as the | |
143 | # system user first. | |
144 | my $system_CF = RT::CustomField->new( RT->SystemUser ); | |
145 | $system_CF->Load( $id ); | |
146 | my $class = $system_CF->RecordClassFromLookupType; | |
147 | ||
148 | my $obj = $class->new( $self->CurrentUser ); | |
149 | $obj->Load( $self->ObjectId ); | |
150 | ||
151 | my $CF = RT::CustomField->new( $self->CurrentUser ); | |
152 | $CF->SetContextObject( $obj ); | |
153 | $CF->Load( $id ); | |
154 | return $CF; | |
155 | } | |
156 | ||
157 | =head2 Sorting custom fields applications | |
158 | ||
159 | Custom fields sorted on multiple layers. First of all custom | |
160 | fields with different lookup type are sorted independently. All | |
161 | global custom fields have fixed order for all objects, but you | |
162 | can insert object specific custom fields between them. Object | |
163 | specific custom fields can be applied to several objects and | |
164 | be on different place. For example you have GCF1, GCF2, LCF1, | |
165 | LCF2 and LCF3 that applies to tickets. You can place GCF2 | |
166 | above GCF1, but they will be in the same order in all queues. | |
167 | However, LCF1 and other local can be placed at any place | |
168 | for particular queue: above global, between them or below. | |
169 | ||
170 | =head3 MoveUp | |
171 | ||
172 | Moves custom field up. See </Sorting custom fields applications>. | |
173 | ||
174 | =cut | |
175 | ||
176 | sub MoveUp { | |
177 | my $self = shift; | |
178 | ||
179 | my $ocfs = RT::ObjectCustomFields->new( $self->CurrentUser ); | |
180 | ||
181 | my $oid = $self->ObjectId; | |
182 | $ocfs->LimitToObjectId( $oid ); | |
183 | if ( $oid ) { | |
184 | $ocfs->LimitToObjectId( 0 ); | |
185 | } | |
186 | ||
187 | my $cf = $self->CustomFieldObj; | |
188 | $ocfs->LimitToLookupType( $cf->LookupType ); | |
189 | ||
190 | $ocfs->Limit( FIELD => 'SortOrder', OPERATOR => '<', VALUE => $self->SortOrder ); | |
191 | $ocfs->OrderByCols( { FIELD => 'SortOrder', ORDER => 'DESC' } ); | |
192 | ||
193 | my @above = ($ocfs->Next, $ocfs->Next); | |
194 | unless ($above[0]) { | |
195 | return (0, "Can not move up. It's already at the top"); | |
196 | } | |
197 | ||
198 | my $new_sort_order; | |
199 | if ( $above[0]->ObjectId == $self->ObjectId ) { | |
200 | $new_sort_order = $above[0]->SortOrder; | |
201 | my ($status, $msg) = $above[0]->SetSortOrder( $self->SortOrder ); | |
202 | unless ( $status ) { | |
203 | return (0, "Couldn't move custom field"); | |
204 | } | |
205 | } | |
206 | elsif ( $above[1] && $above[0]->SortOrder == $above[1]->SortOrder + 1 ) { | |
207 | my $move_ocfs = RT::ObjectCustomFields->new( RT->SystemUser ); | |
208 | $move_ocfs->LimitToLookupType( $cf->LookupType ); | |
209 | $move_ocfs->Limit( | |
210 | FIELD => 'SortOrder', | |
211 | OPERATOR => '>=', | |
212 | VALUE => $above[0]->SortOrder, | |
213 | ); | |
214 | $move_ocfs->OrderByCols( { FIELD => 'SortOrder', ORDER => 'DESC' } ); | |
215 | while ( my $record = $move_ocfs->Next ) { | |
216 | my ($status, $msg) = $record->SetSortOrder( $record->SortOrder + 1 ); | |
217 | unless ( $status ) { | |
218 | return (0, "Couldn't move custom field"); | |
219 | } | |
220 | } | |
221 | $new_sort_order = $above[0]->SortOrder; | |
222 | } else { | |
223 | $new_sort_order = $above[0]->SortOrder - 1; | |
224 | } | |
225 | ||
226 | my ($status, $msg) = $self->SetSortOrder( $new_sort_order ); | |
227 | unless ( $status ) { | |
228 | return (0, "Couldn't move custom field"); | |
229 | } | |
230 | ||
231 | return (1,"Moved custom field up"); | |
232 | } | |
233 | ||
234 | =head3 MoveDown | |
235 | ||
236 | Moves custom field down. See </Sorting custom fields applications>. | |
237 | ||
238 | =cut | |
239 | ||
240 | sub MoveDown { | |
241 | my $self = shift; | |
242 | ||
243 | my $ocfs = RT::ObjectCustomFields->new( $self->CurrentUser ); | |
244 | ||
245 | my $oid = $self->ObjectId; | |
246 | $ocfs->LimitToObjectId( $oid ); | |
247 | if ( $oid ) { | |
248 | $ocfs->LimitToObjectId( 0 ); | |
249 | } | |
250 | ||
251 | my $cf = $self->CustomFieldObj; | |
252 | $ocfs->LimitToLookupType( $cf->LookupType ); | |
253 | ||
254 | $ocfs->Limit( FIELD => 'SortOrder', OPERATOR => '>', VALUE => $self->SortOrder ); | |
255 | $ocfs->OrderByCols( { FIELD => 'SortOrder', ORDER => 'ASC' } ); | |
256 | ||
257 | my @below = ($ocfs->Next, $ocfs->Next); | |
258 | unless ($below[0]) { | |
259 | return (0, "Can not move down. It's already at the bottom"); | |
260 | } | |
261 | ||
262 | my $new_sort_order; | |
263 | if ( $below[0]->ObjectId == $self->ObjectId ) { | |
264 | $new_sort_order = $below[0]->SortOrder; | |
265 | my ($status, $msg) = $below[0]->SetSortOrder( $self->SortOrder ); | |
266 | unless ( $status ) { | |
267 | return (0, "Couldn't move custom field"); | |
268 | } | |
269 | } | |
270 | elsif ( $below[1] && $below[0]->SortOrder + 1 == $below[1]->SortOrder ) { | |
271 | my $move_ocfs = RT::ObjectCustomFields->new( RT->SystemUser ); | |
272 | $move_ocfs->LimitToLookupType( $cf->LookupType ); | |
273 | $move_ocfs->Limit( | |
274 | FIELD => 'SortOrder', | |
275 | OPERATOR => '<=', | |
276 | VALUE => $below[0]->SortOrder, | |
277 | ); | |
278 | $move_ocfs->OrderByCols( { FIELD => 'SortOrder', ORDER => 'ASC' } ); | |
279 | while ( my $record = $move_ocfs->Next ) { | |
280 | my ($status, $msg) = $record->SetSortOrder( $record->SortOrder - 1 ); | |
281 | unless ( $status ) { | |
282 | return (0, "Couldn't move custom field"); | |
283 | } | |
284 | } | |
285 | $new_sort_order = $below[0]->SortOrder; | |
286 | } else { | |
287 | $new_sort_order = $below[0]->SortOrder + 1; | |
288 | } | |
289 | ||
290 | my ($status, $msg) = $self->SetSortOrder( $new_sort_order ); | |
291 | unless ( $status ) { | |
292 | return (0, "Couldn't move custom field"); | |
293 | } | |
294 | ||
295 | return (1,"Moved custom field down"); | |
296 | } | |
297 | ||
298 | ||
299 | =head2 id | |
300 | ||
301 | Returns the current value of id. | |
302 | (In the database, id is stored as int(11).) | |
303 | ||
304 | ||
305 | =cut | |
306 | ||
307 | ||
308 | =head2 CustomField | |
309 | ||
310 | Returns the current value of CustomField. | |
311 | (In the database, CustomField is stored as int(11).) | |
312 | ||
313 | ||
314 | ||
315 | =head2 SetCustomField VALUE | |
316 | ||
317 | ||
318 | Set CustomField to VALUE. | |
319 | Returns (1, 'Status message') on success and (0, 'Error Message') on failure. | |
320 | (In the database, CustomField will be stored as a int(11).) | |
321 | ||
322 | ||
323 | =cut | |
324 | ||
325 | ||
326 | =head2 ObjectId | |
327 | ||
328 | Returns the current value of ObjectId. | |
329 | (In the database, ObjectId is stored as int(11).) | |
330 | ||
331 | ||
332 | ||
333 | =head2 SetObjectId VALUE | |
334 | ||
335 | ||
336 | Set ObjectId to VALUE. | |
337 | Returns (1, 'Status message') on success and (0, 'Error Message') on failure. | |
338 | (In the database, ObjectId will be stored as a int(11).) | |
339 | ||
340 | ||
341 | =cut | |
342 | ||
343 | ||
344 | =head2 SortOrder | |
345 | ||
346 | Returns the current value of SortOrder. | |
347 | (In the database, SortOrder is stored as int(11).) | |
348 | ||
349 | ||
350 | ||
351 | =head2 SetSortOrder VALUE | |
352 | ||
353 | ||
354 | Set SortOrder to VALUE. | |
355 | Returns (1, 'Status message') on success and (0, 'Error Message') on failure. | |
356 | (In the database, SortOrder will be stored as a int(11).) | |
357 | ||
358 | ||
359 | =cut | |
360 | ||
361 | ||
362 | =head2 Creator | |
363 | ||
364 | Returns the current value of Creator. | |
365 | (In the database, Creator is stored as int(11).) | |
366 | ||
367 | ||
368 | =cut | |
369 | ||
370 | ||
371 | =head2 Created | |
372 | ||
373 | Returns the current value of Created. | |
374 | (In the database, Created is stored as datetime.) | |
375 | ||
376 | ||
377 | =cut | |
378 | ||
379 | ||
380 | =head2 LastUpdatedBy | |
381 | ||
382 | Returns the current value of LastUpdatedBy. | |
383 | (In the database, LastUpdatedBy is stored as int(11).) | |
384 | ||
385 | ||
386 | =cut | |
387 | ||
388 | ||
389 | =head2 LastUpdated | |
390 | ||
391 | Returns the current value of LastUpdated. | |
392 | (In the database, LastUpdated is stored as datetime.) | |
393 | ||
394 | ||
395 | =cut | |
396 | ||
397 | ||
398 | ||
399 | sub _CoreAccessible { | |
400 | { | |
401 | ||
402 | id => | |
403 | {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, | |
404 | CustomField => | |
405 | {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, | |
406 | ObjectId => | |
407 | {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''}, | |
408 | SortOrder => | |
409 | {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, | |
410 | Creator => | |
411 | {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, | |
412 | Created => | |
413 | {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, | |
414 | LastUpdatedBy => | |
415 | {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'}, | |
416 | LastUpdated => | |
417 | {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''}, | |
418 | ||
419 | } | |
420 | }; | |
421 | ||
422 | RT::Base->_ImportOverlays(); | |
423 | ||
424 | 1; |