1 # BEGIN BPS TAGGED BLOCK {{{
5 # This software is Copyright (c) 1996-2009 Best Practical Solutions, LLC
6 # <jesse@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 }}}
51 RT::SharedSetting - an API for settings that belong to an RT::User or RT::Group
55 use RT::SharedSetting;
59 A RT::SharedSetting is an object that can belong to an L<RT::User> or an <RT::Group>.
60 It consists of an ID, a name, and some arbitrary data.
64 package RT::SharedSetting;
68 use base qw/RT::Base/;
74 Returns a new L<RT::SharedSetting> object.
75 Takes the current user, see also L<RT::Base>.
81 my $class = ref($proto) || $proto;
84 bless ($self, $class);
85 $self->CurrentUser(@_);
91 Takes a privacy specification and a shared-setting ID. Loads the given object
92 ID if it belongs to the stated user or group. Calls the L</PostLoad> method on
93 success for any further initialization. Returns a tuple of status and message,
94 where status is true on success.
100 my ($privacy, $id) = @_;
101 my $object = $self->_GetObject($privacy);
104 $self->{'Attribute'} = $object->Attributes->WithId($id);
105 if ($self->{'Attribute'}->Id) {
106 $self->{'Id'} = $self->{'Attribute'}->Id;
107 $self->{'Privacy'} = $privacy;
110 return (0, $self->loc("Permission denied"))
111 unless $self->CurrentUserCanSee;
113 return (1, $self->loc("Loaded [_1] [_2]", $self->ObjectName, $self->Name));
115 $RT::Logger->error("Could not load attribute " . $id
116 . " for object " . $privacy);
117 return (0, $self->loc("Failed to load [_1] [_2]", $self->ObjectName, $id))
120 $RT::Logger->warning("Could not load object $privacy when loading " . $self->ObjectName);
121 return (0, $self->loc("Could not load object for [_1]", $privacy));
127 First loads up the L<RT::Attribute> for this shared setting by ID, then calls
128 L</Load> with the correct parameters. Returns a tuple of status and message,
129 where status is true on success.
137 my $attr = RT::Attribute->new($self->CurrentUser);
138 my ($ok, $msg) = $attr->LoadById($id);
141 return (0, $self->loc("Failed to load [_1] [_2]: [_3]", $self->ObjectName, $id, $msg))
144 my $privacy = $self->_build_privacy($attr->ObjectType, $attr->ObjectId);
145 return (0, $self->loc("Bad privacy for attribute [_1]", $id))
148 return $self->Load($privacy, $id);
153 Called after after successful L</Load>.
161 Creates a new shared setting. Takes a privacy, a name, and any other arguments.
162 Saves the given parameters to the appropriate user/group object, and loads the
163 resulting object. Arguments are passed to the L</SaveAttribute> method, which
164 does the actual update. Returns a tuple of status and message, where status is
165 true on success. Defaults are:
167 Privacy: CurrentUser only
168 Name: "new (ObjectName)"
175 'Privacy' => 'RT::User-' . $self->CurrentUser->Id,
176 'Name' => "new " . $self->ObjectName,
180 my $privacy = $args{'Privacy'};
181 my $name = $args{'Name'},
182 my $object = $self->_GetObject($privacy);
184 return (0, $self->loc("Failed to load object for [_1]", $privacy))
187 return (0, $self->loc("Permission denied"))
188 unless $self->CurrentUserCanCreate($privacy);
190 my ($att_id, $att_msg) = $self->SaveAttribute($object, \%args);
193 $self->{'Attribute'} = $object->Attributes->WithId($att_id);
194 $self->{'Id'} = $att_id;
195 $self->{'Privacy'} = $privacy;
196 return ( 1, $self->loc( "Saved [_1] [_2]", $self->ObjectName, $name ) );
199 $RT::Logger->error($self->ObjectName . " save failure: $att_msg");
200 return ( 0, $self->loc("Failed to create [_1] attribute", $self->ObjectName) );
206 An empty method for subclassing. Called from L</Save> method.
210 sub SaveAttribute { }
214 Updates the parameters of an existing shared setting. Any arguments are passed
215 to the L</UpdateAttribute> method. Returns a tuple of status and message, where
216 status is true on success.
224 return(0, $self->loc("No [_1] loaded", $self->ObjectName)) unless $self->Id;
225 return(0, $self->loc("Could not load [_1] attribute", $self->ObjectName))
226 unless $self->{'Attribute'}->Id;
228 return (0, $self->loc("Permission denied"))
229 unless $self->CurrentUserCanModify;
231 my ($status, $msg) = $self->UpdateAttribute(\%args);
233 return (1, $self->loc("[_1] update: Nothing changed", ucfirst($self->ObjectName)))
236 # prevent useless warnings
237 return (1, $self->loc("[_1] updated"), ucfirst($self->ObjectName))
238 if $msg =~ /That is already the current value/;
240 return ($status, $self->loc("[_1] update: [_2]", ucfirst($self->ObjectName), $msg));
243 =head2 UpdateAttribute
245 An empty method for subclassing. Called from L</Update> method.
249 sub UpdateAttribute { }
253 Deletes the existing shared setting. Returns a tuple of status and message,
254 where status is true upon success.
261 return (0, $self->loc("Permission denied"))
262 unless $self->CurrentUserCanDelete;
264 my ($status, $msg) = $self->{'Attribute'}->Delete;
266 return (1, $self->loc("Deleted [_1]", $self->ObjectName));
268 return (0, $self->loc("Delete failed: [_1]", $msg));
276 Returns the name of this shared setting.
282 return unless ref($self->{'Attribute'}) eq 'RT::Attribute';
283 return $self->{'Attribute'}->Description();
288 Returns the numerical ID of this shared setting.
294 return $self->{'Id'};
299 Returns the principal object to whom this shared setting belongs, in a string
300 "<class>-<id>", e.g. "RT::Group-16".
306 return $self->{'Privacy'};
311 Returns the given named parameter of the setting.
318 return unless ref($self->{'Attribute'}) eq 'RT::Attribute';
319 return $self->{'Attribute'}->SubValue($param);
322 =head2 IsVisibleTo Privacy
324 Returns true if the setting is visible to all principals of the given privacy.
325 This does not deal with ACLs, this only looks at membership.
332 my $privacy = $self->Privacy;
334 # if the privacies are the same, then they can be seen. this handles
335 # a personal setting being visible to that user.
336 return 1 if $privacy eq $to;
338 # If the setting is systemwide, then any user can see it.
339 return 1 if $privacy =~ /^RT::System/;
341 # Only privacies that are RT::System can be seen by everyone.
342 return 0 if $to =~ /^RT::System/;
344 # If the setting is group-wide...
345 if ($privacy =~ /^RT::Group-(\d+)$/) {
346 my $setting_group = RT::Group->new($self->CurrentUser);
347 $setting_group->Load($1);
349 if ($to =~ /-(\d+)$/) {
352 # then any principal that is a member of the setting's group can see
354 return $setting_group->HasMemberRecursively($to_id);
361 sub CurrentUserCanSee { 1 }
362 sub CurrentUserCanCreate { 1 }
363 sub CurrentUserCanModify { 1 }
364 sub CurrentUserCanDelete { 1 }
368 # _GetObject: helper routine to load the correct object whose parameters
375 my ($obj_type, $obj_id) = split(/\-/, ($privacy || ''));
377 unless ($obj_type && $obj_id) {
378 $privacy = '(undef)' if !defined($privacy);
379 $RT::Logger->debug("Invalid privacy string '$privacy'");
383 my $object = $self->_load_privacy_object($obj_type, $obj_id);
385 unless (ref($object) eq $obj_type) {
386 $RT::Logger->error("Could not load object of type $obj_type with ID $obj_id, got object of type " . (ref($object) || 'undef'));
390 # Do not allow the loading of a user object other than the current
391 # user, or of a group object of which the current user is not a member.
393 if ($obj_type eq 'RT::User' && $object->Id != $self->CurrentUser->UserObj->Id) {
394 $RT::Logger->debug("Permission denied for user other than self");
398 if ($obj_type eq 'RT::Group' && !$object->HasMemberRecursively($self->CurrentUser->PrincipalObj)) {
399 $RT::Logger->debug("Permission denied, ".$self->CurrentUser->Name.
400 " is not a member of group");
407 sub _load_privacy_object {
408 my ($self, $obj_type, $obj_id) = @_;
409 if ( $obj_type eq 'RT::User' ) {
410 if ( $obj_id == $self->CurrentUser->Id ) {
411 return $self->CurrentUser->UserObj;
413 $RT::Logger->warning("User #". $self->CurrentUser->Id ." tried to load container user #". $obj_id);
417 elsif ($obj_type eq 'RT::Group') {
418 my $group = RT::Group->new($self->CurrentUser);
419 $group->Load($obj_id);
422 elsif ($obj_type eq 'RT::System') {
423 return RT::System->new($self->CurrentUser);
427 "Tried to load a ". $self->ObjectName
428 ." belonging to an $obj_type, which is neither a user nor a group"
435 my ($self, $obj_type, $obj_id) = @_;
437 # allow passing in just an object to find its privacy string
438 if (ref($obj_type)) {
439 my $Object = $obj_type;
440 return $Object->isa('RT::User') ? 'RT::User-' . $Object->Id
441 : $Object->isa('RT::Group') ? 'RT::Group-' . $Object->Id
442 : $Object->isa('RT::System') ? 'RT::System-' . $Object->Id
446 return undef unless ($obj_type); # undef workaround
447 return $obj_type eq 'RT::User' ? "$obj_type-$obj_id"
448 : $obj_type eq 'RT::Group' ? "$obj_type-$obj_id"
449 : $obj_type eq 'RT::System' ? "$obj_type-$obj_id"
453 eval "require RT::SharedSetting_Vendor";
454 die $@ if ($@ && $@ !~ qr{^Can't locate RT/SharedSetting_Vendor.pm});
455 eval "require RT::SharedSetting_Local";
456 die $@ if ($@ && $@ !~ qr{^Can't locate RT/SharedSetting_Local.pm});