starting to work...
[freeside.git] / rt / lib / RT / ObjectCustomField.pm
1 # BEGIN BPS TAGGED BLOCK {{{
2 #
3 # COPYRIGHT:
4 #
5 # This software is Copyright (c) 1996-2012 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     my $CF = RT::CustomField->new( $self->CurrentUser );
141     $CF->Load( $id );
142     return $CF;
143 }
144
145 =head2 Sorting custom fields applications
146
147 Custom fields sorted on multiple layers. First of all custom
148 fields with different lookup type are sorted independently. All
149 global custom fields have fixed order for all objects, but you
150 can insert object specific custom fields between them. Object
151 specific custom fields can be applied to several objects and
152 be on different place. For example you have GCF1, GCF2, LCF1,
153 LCF2 and LCF3 that applies to tickets. You can place GCF2
154 above GCF1, but they will be in the same order in all queues.
155 However, LCF1 and other local can be placed at any place
156 for particular queue: above global, between them or below.
157
158 =head3 MoveUp
159
160 Moves custom field up. See </Sorting custom fields applications>.
161
162 =cut
163
164 sub MoveUp {
165     my $self = shift;
166
167     my $ocfs = RT::ObjectCustomFields->new( $self->CurrentUser );
168
169     my $oid = $self->ObjectId;
170     $ocfs->LimitToObjectId( $oid );
171     if ( $oid ) {
172         $ocfs->LimitToObjectId( 0 );
173     }
174
175     my $cf = $self->CustomFieldObj;
176     $ocfs->LimitToLookupType( $cf->LookupType );
177
178     $ocfs->Limit( FIELD => 'SortOrder', OPERATOR => '<', VALUE => $self->SortOrder );
179     $ocfs->OrderByCols( { FIELD => 'SortOrder', ORDER => 'DESC' } );
180
181     my @above = ($ocfs->Next, $ocfs->Next);
182     unless ($above[0]) {
183         return (0, "Can not move up. It's already at the top");
184     }
185
186     my $new_sort_order;
187     if ( $above[0]->ObjectId == $self->ObjectId ) {
188         $new_sort_order = $above[0]->SortOrder;
189         my ($status, $msg) = $above[0]->SetSortOrder( $self->SortOrder );
190         unless ( $status ) {
191             return (0, "Couldn't move custom field");
192         }
193     }
194     elsif ( $above[1] && $above[0]->SortOrder == $above[1]->SortOrder + 1 ) {
195         my $move_ocfs = RT::ObjectCustomFields->new( RT->SystemUser );
196         $move_ocfs->LimitToLookupType( $cf->LookupType );
197         $move_ocfs->Limit(
198             FIELD => 'SortOrder',
199             OPERATOR => '>=',
200             VALUE => $above[0]->SortOrder,
201         );
202         $move_ocfs->OrderByCols( { FIELD => 'SortOrder', ORDER => 'DESC' } );
203         while ( my $record = $move_ocfs->Next ) {
204             my ($status, $msg) = $record->SetSortOrder( $record->SortOrder + 1 );
205             unless ( $status ) {
206                 return (0, "Couldn't move custom field");
207             }
208         }
209         $new_sort_order = $above[0]->SortOrder;
210     } else {
211         $new_sort_order = $above[0]->SortOrder - 1;
212     }
213
214     my ($status, $msg) = $self->SetSortOrder( $new_sort_order );
215     unless ( $status ) {
216         return (0, "Couldn't move custom field");
217     }
218
219     return (1,"Moved custom field up");
220 }
221
222 =head3 MoveDown
223
224 Moves custom field down. See </Sorting custom fields applications>.
225
226 =cut
227
228 sub MoveDown {
229     my $self = shift;
230
231     my $ocfs = RT::ObjectCustomFields->new( $self->CurrentUser );
232
233     my $oid = $self->ObjectId;
234     $ocfs->LimitToObjectId( $oid );
235     if ( $oid ) {
236         $ocfs->LimitToObjectId( 0 );
237     }
238
239     my $cf = $self->CustomFieldObj;
240     $ocfs->LimitToLookupType( $cf->LookupType );
241
242     $ocfs->Limit( FIELD => 'SortOrder', OPERATOR => '>', VALUE => $self->SortOrder );
243     $ocfs->OrderByCols( { FIELD => 'SortOrder', ORDER => 'ASC' } );
244
245     my @below = ($ocfs->Next, $ocfs->Next);
246     unless ($below[0]) {
247         return (0, "Can not move down. It's already at the bottom");
248     }
249
250     my $new_sort_order;
251     if ( $below[0]->ObjectId == $self->ObjectId ) {
252         $new_sort_order = $below[0]->SortOrder;
253         my ($status, $msg) = $below[0]->SetSortOrder( $self->SortOrder );
254         unless ( $status ) {
255             return (0, "Couldn't move custom field");
256         }
257     }
258     elsif ( $below[1] && $below[0]->SortOrder + 1 == $below[1]->SortOrder ) {
259         my $move_ocfs = RT::ObjectCustomFields->new( RT->SystemUser );
260         $move_ocfs->LimitToLookupType( $cf->LookupType );
261         $move_ocfs->Limit(
262             FIELD => 'SortOrder',
263             OPERATOR => '<=',
264             VALUE => $below[0]->SortOrder,
265         );
266         $move_ocfs->OrderByCols( { FIELD => 'SortOrder', ORDER => 'ASC' } );
267         while ( my $record = $move_ocfs->Next ) {
268             my ($status, $msg) = $record->SetSortOrder( $record->SortOrder - 1 );
269             unless ( $status ) {
270                 return (0, "Couldn't move custom field");
271             }
272         }
273         $new_sort_order = $below[0]->SortOrder;
274     } else {
275         $new_sort_order = $below[0]->SortOrder + 1;
276     }
277
278     my ($status, $msg) = $self->SetSortOrder( $new_sort_order );
279     unless ( $status ) {
280         return (0, "Couldn't move custom field");
281     }
282
283     return (1,"Moved custom field down");
284 }
285
286
287 =head2 id
288
289 Returns the current value of id.
290 (In the database, id is stored as int(11).)
291
292
293 =cut
294
295
296 =head2 CustomField
297
298 Returns the current value of CustomField.
299 (In the database, CustomField is stored as int(11).)
300
301
302
303 =head2 SetCustomField VALUE
304
305
306 Set CustomField to VALUE.
307 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
308 (In the database, CustomField will be stored as a int(11).)
309
310
311 =cut
312
313
314 =head2 ObjectId
315
316 Returns the current value of ObjectId.
317 (In the database, ObjectId is stored as int(11).)
318
319
320
321 =head2 SetObjectId VALUE
322
323
324 Set ObjectId to VALUE.
325 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
326 (In the database, ObjectId will be stored as a int(11).)
327
328
329 =cut
330
331
332 =head2 SortOrder
333
334 Returns the current value of SortOrder.
335 (In the database, SortOrder is stored as int(11).)
336
337
338
339 =head2 SetSortOrder VALUE
340
341
342 Set SortOrder to VALUE.
343 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
344 (In the database, SortOrder will be stored as a int(11).)
345
346
347 =cut
348
349
350 =head2 Creator
351
352 Returns the current value of Creator.
353 (In the database, Creator is stored as int(11).)
354
355
356 =cut
357
358
359 =head2 Created
360
361 Returns the current value of Created.
362 (In the database, Created is stored as datetime.)
363
364
365 =cut
366
367
368 =head2 LastUpdatedBy
369
370 Returns the current value of LastUpdatedBy.
371 (In the database, LastUpdatedBy is stored as int(11).)
372
373
374 =cut
375
376
377 =head2 LastUpdated
378
379 Returns the current value of LastUpdated.
380 (In the database, LastUpdated is stored as datetime.)
381
382
383 =cut
384
385
386
387 sub _CoreAccessible {
388     {
389
390         id =>
391                 {read => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
392         CustomField =>
393                 {read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
394         ObjectId =>
395                 {read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
396         SortOrder =>
397                 {read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
398         Creator =>
399                 {read => 1, auto => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
400         Created =>
401                 {read => 1, auto => 1, sql_type => 11, length => 0,  is_blob => 0,  is_numeric => 0,  type => 'datetime', default => ''},
402         LastUpdatedBy =>
403                 {read => 1, auto => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
404         LastUpdated =>
405                 {read => 1, auto => 1, sql_type => 11, length => 0,  is_blob => 0,  is_numeric => 0,  type => 'datetime', default => ''},
406
407  }
408 };
409
410 RT::Base->_ImportOverlays();
411
412 1;