+=head2 LoadByNameAndObject (Object => OBJECT, Name => NAME)
+
+Loads the Attribute named NAME for Object OBJECT.
+
+=cut
+
+sub LoadByNameAndObject {
+ my $self = shift;
+ my %args = (
+ Object => undef,
+ Name => undef,
+ @_,
+ );
+
+ return (
+ $self->LoadByCols(
+ Name => $args{'Name'},
+ ObjectType => ref($args{'Object'}),
+ ObjectId => $args{'Object'}->Id,
+ )
+ );
+
+}
+
+
+
+=head2 _DeserializeContent
+
+DeserializeContent returns this Attribute's "Content" as a hashref.
+
+
+=cut
+
+sub _DeserializeContent {
+ my $self = shift;
+ my $content = shift;
+
+ my $hashref;
+ eval {$hashref = thaw(decode_base64($content))} ;
+ if ($@) {
+ $RT::Logger->error("Deserialization of attribute ".$self->Id. " failed");
+ }
+
+ return($hashref);
+
+}
+
+
+=head2 Content
+
+Returns this attribute's content. If it's a scalar, returns a scalar
+If it's data structure returns a ref to that data structure.
+
+=cut
+
+sub Content {
+ my $self = shift;
+ # Here we call _Value to get the ACL check.
+ my $content = $self->_Value('Content');
+ if ( ($self->__Value('ContentType') || '') eq 'storable') {
+ eval {$content = $self->_DeserializeContent($content); };
+ if ($@) {
+ $RT::Logger->error("Deserialization of content for attribute ".$self->Id. " failed. Attribute was: ".$content);
+ }
+ }
+
+ return($content);
+
+}
+
+sub _SerializeContent {
+ my $self = shift;
+ my $content = shift;
+ return( encode_base64(nfreeze($content)));
+}
+
+
+sub SetContent {
+ my $self = shift;
+ my $content = shift;
+
+ # Call __Value to avoid ACL check.
+ if ( ($self->__Value('ContentType')||'') eq 'storable' ) {
+ # We eval the serialization because it will lose on a coderef.
+ $content = eval { $self->_SerializeContent($content) };
+ if ($@) {
+ $RT::Logger->error("Content couldn't be frozen: $@");
+ return(0, "Content couldn't be frozen");
+ }
+ }
+ my ($ok, $msg) = $self->_Set( Field => 'Content', Value => $content );
+ return ($ok, $self->loc("Attribute updated")) if $ok;
+ return ($ok, $msg);
+}
+
+=head2 SubValue KEY
+
+Returns the subvalue for $key.
+
+
+=cut
+
+sub SubValue {
+ my $self = shift;
+ my $key = shift;
+ my $values = $self->Content();
+ return undef unless ref($values);
+ return($values->{$key});
+}
+
+=head2 DeleteSubValue NAME
+
+Deletes the subvalue with the key NAME
+
+=cut
+
+sub DeleteSubValue {
+ my $self = shift;
+ my $key = shift;
+ my $values = $self->Content();
+ delete $values->{$key};
+ $self->SetContent($values);
+}
+
+
+=head2 DeleteAllSubValues
+
+Deletes all subvalues for this attribute
+
+=cut
+
+
+sub DeleteAllSubValues {
+ my $self = shift;
+ $self->SetContent({});
+}
+
+=head2 SetSubValues { }
+
+Takes a hash of keys and values and stores them in the content of this attribute.
+
+Each key B<replaces> the existing key with the same name
+
+Returns a tuple of (status, message)
+
+=cut
+
+
+sub SetSubValues {
+ my $self = shift;
+ my %args = (@_);
+ my $values = ($self->Content() || {} );
+ foreach my $key (keys %args) {
+ $values->{$key} = $args{$key};
+ }
+
+ $self->SetContent($values);
+
+}
+
+
+sub Object {
+ my $self = shift;
+ my $object_type = $self->__Value('ObjectType');
+ my $object;
+ eval { $object = $object_type->new($self->CurrentUser) };
+ unless(UNIVERSAL::isa($object, $object_type)) {
+ $RT::Logger->error("Attribute ".$self->Id." has a bogus object type - $object_type (".$@.")");
+ return(undef);
+ }
+ $object->Load($self->__Value('ObjectId'));
+
+ return($object);
+
+}
+
+
+sub Delete {
+ my $self = shift;
+ unless ($self->CurrentUserHasRight('delete')) {
+ return (0,$self->loc('Permission Denied'));
+ }
+
+ return($self->SUPER::Delete(@_));
+}
+
+
+sub _Value {
+ my $self = shift;
+ unless ($self->CurrentUserHasRight('display')) {
+ return (0,$self->loc('Permission Denied'));
+ }
+
+ return($self->SUPER::_Value(@_));
+
+
+}
+
+
+sub _Set {
+ my $self = shift;
+ unless ($self->CurrentUserHasRight('update')) {
+
+ return (0,$self->loc('Permission Denied'));
+ }
+ return($self->SUPER::_Set(@_));
+
+}
+
+
+=head2 CurrentUserHasRight
+
+One of "display" "update" "delete" or "create" and returns 1 if the user has that right for attributes of this name for this object.Returns undef otherwise.
+
+=cut
+
+sub CurrentUserHasRight {
+ my $self = shift;
+ my $right = shift;
+
+ # object_right is the right that the user has to have on the object for them to have $right on this attribute
+ my $object_right = $self->LookupObjectRight(
+ Right => $right,
+ ObjectId => $self->__Value('ObjectId'),
+ ObjectType => $self->__Value('ObjectType'),
+ Name => $self->__Value('Name')
+ );
+
+ return (1) if ($object_right eq 'allow');
+ return (0) if ($object_right eq 'deny');
+ return(1) if ($self->CurrentUser->HasRight( Object => $self->Object, Right => $object_right));
+ return(0);
+
+}
+
+
+=head1 TODO
+
+We should be deserializing the content on load and then enver again, rather than at every access
+
+=cut
+
+
+
+
+
+
+
+