[Rt-commit] rt branch, 4.4/dashboard-tables, created. rt-4.2.5-200-ge5ac318
? sunnavy
sunnavy at bestpractical.com
Mon Sep 29 08:58:41 EDT 2014
The branch, 4.4/dashboard-tables has been created
at e5ac318b928c3297c831a46a703a7c8388512e10 (commit)
- Log -----------------------------------------------------------------
commit e5ac318b928c3297c831a46a703a7c8388512e10
Author: sunnavy <sunnavy at bestpractical.com>
Date: Mon Sep 29 20:53:50 2014 +0800
split dashboard into its own table
diff --git a/etc/acl.Pg b/etc/acl.Pg
index a659d8e..190ec40 100755
--- a/etc/acl.Pg
+++ b/etc/acl.Pg
@@ -58,6 +58,8 @@ sub acl {
ObjectTopics
objectclasses_id_seq
ObjectClasses
+ dashboards_id_seq
+ Dashboards
);
my $db_user = RT->Config->Get('DatabaseUser');
diff --git a/etc/schema.Oracle b/etc/schema.Oracle
index 58665c7..ad4f88b 100755
--- a/etc/schema.Oracle
+++ b/etc/schema.Oracle
@@ -488,3 +488,16 @@ Created DATE,
LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
LastUpdated DATE
);
+
+CREATE SEQUENCE Dashboards_seq;
+CREATE TABLE Dashboards (
+ id NUMBER(11,0) PRIMARY KEY,
+ Name VARCHAR2(255) NOT NULL,
+ Content CLOB,
+ ObjectType VARCHAR2(25) NOT NULL,
+ ObjectId NUMBER(11,0) DEFAULT 0 NOT NULL,
+ Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
+ Created DATE,
+ LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
+ LastUpdated DATE
+);
diff --git a/etc/schema.Pg b/etc/schema.Pg
index 356441b..078b5da 100755
--- a/etc/schema.Pg
+++ b/etc/schema.Pg
@@ -720,3 +720,17 @@ LastUpdated TIMESTAMP NULL,
PRIMARY KEY (id)
);
+CREATE SEQUENCE dashboards_id_seq;
+CREATE TABLE Dashboards (
+ id INTEGER DEFAULT nextval('dashboards_id_seq'),
+ Name varchar(255) NOT NULL,
+ Content text,
+ ObjectType varchar(64),
+ ObjectId integer,
+ Creator integer NOT NULL DEFAULT 0,
+ Created TIMESTAMP NULL,
+ LastUpdatedBy integer NOT NULL DEFAULT 0,
+ LastUpdated TIMESTAMP NULL,
+ PRIMARY KEY (id)
+);
+
diff --git a/etc/schema.SQLite b/etc/schema.SQLite
index 7ba11f7..dfb9245 100755
--- a/etc/schema.SQLite
+++ b/etc/schema.SQLite
@@ -520,3 +520,15 @@ Created TIMESTAMP NULL,
LastUpdatedBy integer NOT NULL DEFAULT 0,
LastUpdated TIMESTAMP NULL
);
+
+CREATE TABLE Dashboards (
+ id INTEGER PRIMARY KEY,
+ Name varchar(255) NOT NULL,
+ Content LONGTEXT NULL,
+ ObjectType varchar(25) NOT NULL,
+ ObjectId INTEGER default 0,
+ Creator integer NULL,
+ Created DATETIME NULL,
+ LastUpdatedBy integer NULL,
+ LastUpdated DATETIME NULL
+);
diff --git a/etc/schema.mysql b/etc/schema.mysql
index 21ff5cb..e77a664 100755
--- a/etc/schema.mysql
+++ b/etc/schema.mysql
@@ -509,3 +509,16 @@ CREATE TABLE ObjectClasses (
LastUpdated datetime default NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE Dashboards (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ Name varchar(255) NULL,
+ Content BLOB,
+ ObjectType varchar(64) CHARACTER SET ascii,
+ ObjectId integer,
+ Creator integer NOT NULL DEFAULT 0,
+ Created DATETIME NULL,
+ LastUpdatedBy integer NOT NULL DEFAULT 0,
+ LastUpdated DATETIME NULL,
+ PRIMARY KEY (id)
+) ENGINE=InnoDB CHARACTER SET utf8;
diff --git a/etc/upgrade/4.3.2/acl.Oracle b/etc/upgrade/4.3.2/acl.Oracle
new file mode 100644
index 0000000..73c16ae
--- /dev/null
+++ b/etc/upgrade/4.3.2/acl.Oracle
@@ -0,0 +1,4 @@
+sub acl {
+ return ();
+}
+1;
diff --git a/etc/upgrade/4.3.2/acl.Pg b/etc/upgrade/4.3.2/acl.Pg
new file mode 100644
index 0000000..b37d760
--- /dev/null
+++ b/etc/upgrade/4.3.2/acl.Pg
@@ -0,0 +1,19 @@
+sub acl {
+ my $dbh = shift;
+
+ my @acls;
+
+ my @tables = qw (
+ dashboards_id_seq
+ Dashboards
+ );
+
+ foreach my $table (@tables) {
+ push @acls,
+ "GRANT SELECT, INSERT, UPDATE, DELETE ON $table to "
+ . RT->Config->Get('DatabaseUser') . ";";
+
+ }
+ return (@acls);
+}
+1;
diff --git a/etc/upgrade/4.3.2/acl.SQLite b/etc/upgrade/4.3.2/acl.SQLite
new file mode 100644
index 0000000..73c16ae
--- /dev/null
+++ b/etc/upgrade/4.3.2/acl.SQLite
@@ -0,0 +1,4 @@
+sub acl {
+ return ();
+}
+1;
diff --git a/etc/upgrade/4.3.2/acl.mysql b/etc/upgrade/4.3.2/acl.mysql
new file mode 100644
index 0000000..73c16ae
--- /dev/null
+++ b/etc/upgrade/4.3.2/acl.mysql
@@ -0,0 +1,4 @@
+sub acl {
+ return ();
+}
+1;
diff --git a/etc/upgrade/4.3.2/content b/etc/upgrade/4.3.2/content
new file mode 100644
index 0000000..098e0ef
--- /dev/null
+++ b/etc/upgrade/4.3.2/content
@@ -0,0 +1,86 @@
+use strict;
+use warnings;
+my %new_id_for;
+
+my $convert_attributes = sub {
+ my $obj = shift;
+
+ for my $attr ($obj->Attributes->Named('Dashboard')) {
+ my $attr_id = $attr->id;
+
+ my $dashboard = RT::Dashboard->new($RT::SystemUser);
+
+ my ($ok, $msg) = $dashboard->Create(
+ Name => $attr->Description,
+ Content => $attr->Content,
+ ObjectType => ref($obj),
+ ObjectId => $obj->id,
+ );
+
+ if (!$ok) {
+ $RT::Logger->error("Unable to reify attribute $attr_id into a dashboard: $msg");
+ next;
+ }
+
+ $new_id_for{$attr_id} = $dashboard->id;
+
+ ($ok, $msg) = $attr->Delete;
+
+ if (!$ok) {
+ $RT::Logger->error("Reified attribute $attr_id into dashboard " . $dashboard->id . " but couldn't delete attribute: $msg");
+ next;
+ }
+
+ $RT::Logger->debug("Reified attribute $attr_id into a dashboard: $msg");
+ }
+};
+
+our @Final = (
+ sub {
+ my $users = RT::Users->new($RT::SystemUser);
+ $users->UnLimit;
+
+ while (my $user = $users->Next) {
+ $convert_attributes->($user);
+ }
+ },
+ sub {
+ my $groups = RT::Groups->new($RT::SystemUser);
+ $groups->UnLimit;
+
+ while (my $group = $groups->Next) {
+ $convert_attributes->($group);
+ }
+ },
+ sub {
+ my $system = RT::System->new($RT::SystemUser);
+ $convert_attributes->($system);
+ },
+
+ sub {
+ my $users = RT::Users->new($RT::SystemUser);
+ $users->UnLimit;
+
+ while (my $user = $users->Next) {
+ for my $subscription ($users->Attributes->Named('Subscription')) {
+ my $old_id = $subscription->SubValue('DashboardId');
+ my $new_id = $new_id_for{$old_id};
+
+ if (!$new_id) {
+ $RT::Logger->error("Can't update subscription " . $subscription->id . " for old-style dashboard $old_id since it has no new-style ID");
+ next;
+ }
+
+ my ($ok, $msg) = $subscription->SetSubValue(DashboardId => $new_id);
+
+ if (!$ok) {
+ $RT::Logger->error("Unable to update subscription " . $subscription->id . " from dashboard $old_id to $new_id: $msg");
+ next;
+ }
+
+ $RT::Logger->debug("Updated subscription " . $subscription->id . " from dashboard $old_id to $new_id");
+ }
+ }
+ },
+);
+
diff --git a/etc/upgrade/4.3.2/schema.Oracle b/etc/upgrade/4.3.2/schema.Oracle
new file mode 100644
index 0000000..44e6714
--- /dev/null
+++ b/etc/upgrade/4.3.2/schema.Oracle
@@ -0,0 +1,12 @@
+CREATE SEQUENCE Dashboards_seq;
+CREATE TABLE Dashboards (
+ id NUMBER(11,0) PRIMARY KEY,
+ Name VARCHAR2(255) NOT NULL,
+ Content CLOB,
+ ObjectType VARCHAR2(25) NOT NULL,
+ ObjectId NUMBER(11,0) DEFAULT 0 NOT NULL,
+ Creator NUMBER(11,0) DEFAULT 0 NOT NULL,
+ Created DATE,
+ LastUpdatedBy NUMBER(11,0) DEFAULT 0 NOT NULL,
+ LastUpdated DATE
+);
diff --git a/etc/upgrade/4.3.2/schema.Pg b/etc/upgrade/4.3.2/schema.Pg
new file mode 100644
index 0000000..18f6854
--- /dev/null
+++ b/etc/upgrade/4.3.2/schema.Pg
@@ -0,0 +1,13 @@
+CREATE SEQUENCE dashboards_id_seq;
+CREATE TABLE Dashboards (
+ id INTEGER DEFAULT nextval('dashboards_id_seq'),
+ Name varchar(255) NOT NULL,
+ Content text,
+ ObjectType varchar(64),
+ ObjectId integer,
+ Creator integer NOT NULL DEFAULT 0,
+ Created TIMESTAMP NULL,
+ LastUpdatedBy integer NOT NULL DEFAULT 0,
+ LastUpdated TIMESTAMP NULL,
+ PRIMARY KEY (id)
+);
diff --git a/etc/upgrade/4.3.2/schema.SQLite b/etc/upgrade/4.3.2/schema.SQLite
new file mode 100644
index 0000000..ac02600
--- /dev/null
+++ b/etc/upgrade/4.3.2/schema.SQLite
@@ -0,0 +1,11 @@
+CREATE TABLE Dashboards (
+ id INTEGER PRIMARY KEY,
+ Name varchar(255) NOT NULL,
+ Content LONGTEXT NULL,
+ ObjectType varchar(25) NOT NULL,
+ ObjectId INTEGER default 0,
+ Creator integer NULL,
+ Created DATETIME NULL,
+ LastUpdatedBy integer NULL,
+ LastUpdated DATETIME NULL
+);
diff --git a/etc/upgrade/4.3.2/schema.mysql b/etc/upgrade/4.3.2/schema.mysql
new file mode 100644
index 0000000..9a98cef
--- /dev/null
+++ b/etc/upgrade/4.3.2/schema.mysql
@@ -0,0 +1,12 @@
+CREATE TABLE Dashboards (
+ id INTEGER NOT NULL AUTO_INCREMENT,
+ Name varchar(255) NULL,
+ Content BLOB,
+ ObjectType varchar(64) CHARACTER SET ascii,
+ ObjectId integer,
+ Creator integer NOT NULL DEFAULT 0,
+ Created DATETIME NULL,
+ LastUpdatedBy integer NOT NULL DEFAULT 0,
+ LastUpdated DATETIME NULL,
+ PRIMARY KEY (id)
+) ENGINE=InnoDB CHARACTER SET utf8;
diff --git a/lib/RT/Dashboard.pm b/lib/RT/Dashboard.pm
index 6b9244f..bae7ba8 100644
--- a/lib/RT/Dashboard.pm
+++ b/lib/RT/Dashboard.pm
@@ -48,18 +48,14 @@
=head1 NAME
- RT::Dashboard - an API for saving and retrieving dashboards
+ RT::Dashboard - Dashboard
=head1 SYNOPSIS
- use RT::Dashboard
+ use RT::Dashboard;
=head1 DESCRIPTION
- Dashboard is an object that can belong to either an RT::User or an
- RT::Group. It consists of an ID, a name, and a number of
- saved searches and portlets.
-
=head1 METHODS
@@ -70,11 +66,16 @@ package RT::Dashboard;
use strict;
use warnings;
-use base qw/RT::SharedSetting/;
+use base 'RT::Record';
use RT::SavedSearch;
-
use RT::System;
+use Storable qw/nfreeze thaw/;
+use MIME::Base64;
+use Scalar::Util 'blessed';
+
+sub Table { 'Dashboards' }
+
'RT::System'->AddRight( Staff => SubscribeDashboard => 'Subscribe to dashboards'); # loc
'RT::System'->AddRight( General => SeeDashboard => 'View system dashboards'); # loc
@@ -87,71 +88,30 @@ use RT::System;
'RT::System'->AddRight( Staff => ModifyOwnDashboard => 'Modify personal dashboards'); # loc
'RT::System'->AddRight( Staff => DeleteOwnDashboard => 'Delete personal dashboards'); # loc
+=head2 Create
-=head2 ObjectName
-
-An object of this class is called "dashboard"
+Accepts a C<Privacy> instead of an C<ObjectType> and C<ObjectId>.
=cut
-sub ObjectName { "dashboard" } # loc
-
-sub SaveAttribute {
- my $self = shift;
- my $object = shift;
- my $args = shift;
-
- return $object->AddAttribute(
- 'Name' => 'Dashboard',
- 'Description' => $args->{'Name'},
- 'Content' => {Panes => $args->{'Panes'}},
- );
-}
-
-sub UpdateAttribute {
+sub Create {
my $self = shift;
- my $args = shift;
+ my %args = ( Content => {}, @_ );
- my ($status, $msg) = (1, undef);
- if (defined $args->{'Panes'}) {
- ($status, $msg) = $self->{'Attribute'}->SetSubValues(
- Panes => $args->{'Panes'},
- );
- }
- if ($status && $args->{'Name'}) {
- ($status, $msg) = $self->{'Attribute'}->SetDescription($args->{'Name'})
- unless $self->Name eq $args->{'Name'};
+ eval {$args{'Content'} = $self->_SerializeContent($args{'Content'}); };
+ if ($@) {
+ return(0, $@);
}
- if ($status && $args->{'Privacy'}) {
- my ($new_obj_type, $new_obj_id) = split /-/, $args->{'Privacy'};
- my ($obj_type, $obj_id) = split /-/, $self->Privacy;
-
- my $attr = $self->{'Attribute'};
- if ($new_obj_type ne $obj_type) {
- ($status, $msg) = $attr->SetObjectType($new_obj_type);
- }
- if ($status && $new_obj_id != $obj_id ) {
- ($status, $msg) = $attr->SetObjectId($new_obj_id);
- }
- $self->{'Privacy'} = $args->{'Privacy'} if $status;
+ # canonicalize Privacy into ObjectType and ObjectId
+ if ($args{Privacy}) {
+ ($args{ObjectType}, $args{ObjectId}) = split '-', delete $args{Privacy};
}
- return ($status, $msg);
-}
-
-=head2 PostLoadValidate
-
-Ensure that the ID corresponds to an actual dashboard object, since it's all
-attributes under the hood.
-
-=cut
-
-sub PostLoadValidate {
- my $self = shift;
- return (0, "Invalid object type") unless $self->{'Attribute'}->Name eq 'Dashboard';
- return 1;
+ my ( $ret, $msg ) = $self->SUPER::Create(%args);
+ return ( $ret, $msg ) unless $ret;
+ return ( $ret, $self->loc('Dashboard [_1] created',$self->id) );
}
=head2 Panes
@@ -162,8 +122,13 @@ Returns a hashref of pane name to portlets
sub Panes {
my $self = shift;
- return unless ref($self->{'Attribute'}) eq 'RT::Attribute';
- return $self->{'Attribute'}->SubValue('Panes') || {};
+ return $self->Content->{Panes} || {};
+}
+
+sub SetPanes {
+ my $self = shift;
+ my $panes = shift || {};
+ return $self->SetContent({ %{$self->Content}, Panes => $panes });
}
=head2 Portlets
@@ -390,22 +355,17 @@ sub ObjectsForLoading {
Recursively => 1,
PrincipalId => $CurrentUser->UserObj->PrincipalId
);
- my $attrs = $groups->Join(
+ my $dashboards = $groups->Join(
ALIAS1 => 'main',
FIELD1 => 'id',
- TABLE2 => 'Attributes',
+ TABLE2 => 'Dashboards',
FIELD2 => 'ObjectId',
);
$groups->Limit(
- ALIAS => $attrs,
+ ALIAS => $dashboards,
FIELD => 'ObjectType',
VALUE => 'RT::Group',
);
- $groups->Limit(
- ALIAS => $attrs,
- FIELD => 'Name',
- VALUE => 'Dashboard',
- );
push @objects, @{ $groups->ItemsArrayRef };
# Finally, if you have been granted the SeeDashboard right (which
@@ -449,6 +409,10 @@ Returns a tuple of status and message, where status is true upon success.
sub Delete {
my $self = shift;
my $id = $self->id;
+ unless ($self->CurrentUserCanDelete) {
+ return (0,$self->loc('Permission Denied'));
+ }
+
my ( $status, $msg ) = $self->SUPER::Delete(@_);
if ( $status ) {
# delete all the subscriptions
@@ -464,11 +428,268 @@ sub Delete {
while ( my $subscription = $subscriptions->Next ) {
$subscription->Delete();
}
+ return ( $status, $self->loc('Dashboard [_1] deleted', $id) );
}
return ( $status, $msg );
}
+sub Object {
+ my $self = shift;
+ return unless $self->__Value('ObjectId');
+ my $Object = $self->__Value('ObjectType')->new($self->CurrentUser);
+ $Object->Load($self->__Value('ObjectId'));
+ return $Object;
+}
+
+sub Content {
+ my $self = shift;
+ my $content = $self->_Value('Content');
+ return $self->_DeserializeContent($content);
+}
+
+sub SetContent {
+ my $self = shift;
+ my $content = shift;
+ return $self->_Set( Field => 'Content', Value => $self->_SerializeContent( $content ) );
+}
+
+
+sub _SerializeContent {
+ my $self = shift;
+ my $content = shift;
+ return encode_base64(nfreeze($content));
+}
+
+sub _DeserializeContent {
+ my $self = shift;
+ my $content = shift;
+ return thaw(decode_base64($content));
+}
+
+sub _build_privacy {
+ my $self = shift;
+ my $object = shift || $self->Object;
+ return undef unless $object;
+ return ref($object) . '-' . $object->id;
+}
+
+sub _load_privacy_object {
+ my ($self, $obj_type, $obj_id) = @_;
+ if ( $obj_type eq 'RT::User' ) {
+ if ( $obj_id == $self->CurrentUser->Id ) {
+ return $self->CurrentUser->UserObj;
+ } else {
+ $RT::Logger->warning("User #". $self->CurrentUser->Id ." tried to load container user #". $obj_id);
+ return undef;
+ }
+ }
+ elsif ($obj_type eq 'RT::Group') {
+ my $group = RT::Group->new($self->CurrentUser);
+ $group->Load($obj_id);
+ return $group;
+ }
+ elsif ($obj_type eq 'RT::System') {
+ return RT::System->new($self->CurrentUser);
+ }
+
+ $RT::Logger->error(
+ "Tried to load a ". $self->ObjectName
+ ." belonging to an $obj_type, which is neither a user nor a group"
+ );
+
+ return undef;
+}
+
+sub _GetObject {
+ my $self = shift;
+ my $privacy = shift;
+
+ # short circuit: if they pass the object we want anyway, just return it
+ if (blessed($privacy) && $privacy->isa('RT::Record')) {
+ return $privacy;
+ }
+
+ my ($obj_type, $obj_id) = split(/\-/, ($privacy || ''));
+
+ unless ($obj_type && $obj_id) {
+ $privacy = '(undef)' if !defined($privacy);
+ $RT::Logger->debug("Invalid privacy string '$privacy'");
+ return undef;
+ }
+
+ my $object = $self->_load_privacy_object($obj_type, $obj_id);
+
+ unless (ref($object) eq $obj_type) {
+ $RT::Logger->error("Could not load object of type $obj_type with ID $obj_id, got object of type " . (ref($object) || 'undef'));
+ return undef;
+ }
+
+ # Do not allow the loading of a user object other than the current
+ # user, or of a group object of which the current user is not a member.
+
+ if ($obj_type eq 'RT::User' && $object->Id != $self->CurrentUser->UserObj->Id) {
+ $RT::Logger->debug("Permission denied for user other than self");
+ return undef;
+ }
+
+ if ( $obj_type eq 'RT::Group'
+ && !$object->HasMemberRecursively($self->CurrentUser->PrincipalObj)
+ && !$self->CurrentUser->HasRight( Object => $RT::System, Right => 'SuperUser' ) ) {
+ $RT::Logger->debug("Permission denied, ".$self->CurrentUser->Name.
+ " is not a member of group");
+ return undef;
+ }
+
+ return $object;
+}
+
+sub Privacy {
+ my $self = shift;
+ return $self->_build_privacy;
+}
+
+sub SetPrivacy {
+ my $self = shift;
+ my $privacy = shift;
+ my ($object_type, $object_id) = split '-', $privacy, 2;
+ $RT::Handle->BeginTransaction();
+ if ( $self->ObjectType ne $object_type ) {
+ my ($ret, $msg) = $self->SetObjectType($object_type);
+ unless ( $ret ) {
+ $RT::Handle->Rollback();
+ return ($ret, $msg);
+ }
+ }
+
+ if ( $self->ObjectId != $object_id ) {
+ my ($ret, $msg) = $self->SetObjectId($object_id);
+ unless ( $ret ) {
+ $RT::Handle->Rollback();
+ return ($ret, $msg);
+ }
+ }
+ $RT::Handle->Commit();
+ return( 1, 'Privacy updated' ); # loc
+}
+
+sub IsVisibleTo {
+ my $self = shift;
+ my $to = shift;
+ my $privacy = $self->Privacy || '';
+
+ # if the privacies are the same, then they can be seen. this handles
+ # a personal setting being visible to that user.
+ return 1 if $privacy eq $to;
+
+ # If the setting is systemwide, then any user can see it.
+ return 1 if $privacy =~ /^RT::System/;
+
+ # Only privacies that are RT::System can be seen by everyone.
+ return 0 if $to =~ /^RT::System/;
+
+ # If the setting is group-wide...
+ if ($privacy =~ /^RT::Group-(\d+)$/) {
+ my $setting_group = RT::Group->new($self->CurrentUser);
+ $setting_group->Load($1);
+
+ if ($to =~ /-(\d+)$/) {
+ my $to_id = $1;
+
+ # then any principal that is a member of the setting's group can see
+ # the setting
+ return $setting_group->HasMemberRecursively($to_id);
+ }
+ }
+
+ return 0;
+}
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+=head2 Name
+
+Returns the current value of Name.
+(In the database, Name is stored as varchar(255).)
+
+=head2 SetName VALUE
+
+Set Name to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Name will be stored as a varchar(255).)
+
+=head2 Content
+
+Returns the current value of Content.
+(In the database, Content is stored as blob.)
+
+=head2 SetContent VALUE
+
+Set Content to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Content will be stored as a blob.)
+
+=head2 ObjectType
+
+Returns the current value of ObjectType.
+(In the database, ObjectType is stored as varchar(64).)
+
+=head2 SetObjectType VALUE
+
+Set ObjectType to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectType will be stored as a varchar(64).)
+
+=head2 ObjectId
+
+Returns the current value of ObjectId.
+(In the database, ObjectId is stored as int(11).)
+
+=head2 SetObjectId VALUE
+
+Set ObjectId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectId will be stored as a int(11).)
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+=head2 LastUpdatedBy
+
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
+
+=head2 LastUpdated
+
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
+
+=cut
+
+sub _CoreAccessible {
+ {
+ id => {read => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Name => {read => 1, write => 1, sql_type => 12, length => 255, is_blob => 0, is_numeric => 0, type => 'varchar(255)', default => ''},
+ Content => {read => 1, write => 1, sql_type => -4, length => 0, is_blob => 1, is_numeric => 0, type => 'blob', default => ''},
+ ObjectType => {read => 1, write => 1, sql_type => 12, length => 64, is_blob => 0, is_numeric => 0, type => 'varchar(64)', default => ''},
+ ObjectId => {read => 1, write => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => ''},
+ Creator => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ Created => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ LastUpdatedBy => {read => 1, auto => 1, sql_type => 4, length => 11, is_blob => 0, is_numeric => 1, type => 'int(11)', default => '0'},
+ LastUpdated => {read => 1, auto => 1, sql_type => 11, length => 0, is_blob => 0, is_numeric => 0, type => 'datetime', default => ''},
+ }
+};
+
RT::Base->_ImportOverlays();
1;
diff --git a/lib/RT/Dashboards.pm b/lib/RT/Dashboards.pm
index 7c8511f..ccdce96 100644
--- a/lib/RT/Dashboards.pm
+++ b/lib/RT/Dashboards.pm
@@ -48,17 +48,11 @@
=head1 NAME
- RT::Dashboards - a pseudo-collection for Dashboard objects.
+ RT::Dashboards - a collection for Dashboard objects
=head1 SYNOPSIS
- use RT::Dashboards
-
-=head1 DESCRIPTION
-
- Dashboards is an object consisting of a number of Dashboard objects.
- It works more or less like a DBIx::SearchBuilder collection, although it
- is not.
+ use RT::Dashboards;
=head1 METHODS
@@ -69,43 +63,44 @@ package RT::Dashboards;
use strict;
use warnings;
-use base 'RT::SharedSettings';
+
+use base 'RT::SearchBuilder';
use RT::Dashboard;
-sub RecordClass {
- return 'RT::Dashboard';
-}
+sub Table { 'Dashboards' }
-=head2 LimitToPrivacy
+=head2 LimitToObject
-Takes one argument: a privacy string, of the format "<class>-<id>", as produced
-by RT::Dashboard::Privacy(). The Dashboards object will load the dashboards
-belonging to that user or group. Repeated calls to the same object should DTRT.
+The Dashboards object will load the dashboards belonging to the passed-in user
+or group. Repeated calls to the same object should DTRT.
=cut
-sub LimitToPrivacy {
+sub LimitToObject {
my $self = shift;
- my $privacy = shift;
-
- my $object = $self->_GetObject($privacy);
-
- if ($object) {
- $self->{'objects'} = [];
- my @dashboard_atts = $object->Attributes->Named('Dashboard');
- foreach my $att (@dashboard_atts) {
- my $dashboard = RT::Dashboard->new($self->CurrentUser);
- $dashboard->Load($privacy, $att->Id);
- push(@{$self->{'objects'}}, $dashboard);
- }
- } else {
- $RT::Logger->error("Could not load object $privacy");
- }
+ my $obj = shift;
+
+ $self->Limit(
+ FIELD => 'ObjectType',
+ VALUE => ref($obj),
+ );
+
+ $self->Limit(
+ FIELD => 'ObjectId',
+ VALUE => $obj->id,
+ );
}
-sub ColumnMapClassName {
- return 'RT__Dashboard';
+=head2 NewItem
+
+Returns an empty new L<RT::Dashboard> record.
+
+=cut
+
+sub NewItem {
+ my $self = shift;
+ return RT::Dashboard->new($self->CurrentUser);
}
RT::Base->_ImportOverlays();
diff --git a/share/html/Admin/Global/DashboardsInMenu.html b/share/html/Admin/Global/DashboardsInMenu.html
index 279c022..00bd148 100644
--- a/share/html/Admin/Global/DashboardsInMenu.html
+++ b/share/html/Admin/Global/DashboardsInMenu.html
@@ -69,7 +69,7 @@ my $default_dashboards_in_menu =
use RT::Dashboards;
my $dashboards = RT::Dashboards->new( $RT::SystemUser );
-$dashboards->LimitToPrivacy('RT::System-' . $sys->id);
+$dashboards->LimitToObject($sys);
my @dashboards;
while ( my $dashboard = $dashboards->Next ) {
diff --git a/share/html/Dashboards/Elements/DashboardsForObject b/share/html/Dashboards/Elements/DashboardsForObject
index bffdc1a..9439669 100644
--- a/share/html/Dashboards/Elements/DashboardsForObject
+++ b/share/html/Dashboards/Elements/DashboardsForObject
@@ -52,28 +52,21 @@ $Object => undef
# Returns a hash of dashboards associated on $Object
use RT::Dashboard;
+use RT::Dashboards;
my %dashboards;
my $privacy = RT::Dashboard->_build_privacy($Object);
-while (my $attr = $Object->Attributes->Next) {
- if ($attr->Name =~ /^Dashboard\b/) {
- my $dashboard = RT::Dashboard->new($User);
- my ($ok, $msg) = $dashboard->Load($privacy, $attr->id);
-
- if (!$ok) {
- $RT::Logger->debug("Unable to load dashboard $ok (privacy $privacy): $msg");
- next;
- }
-
- if ($Object->isa('RT::System')) {
- push @{ $dashboards{system} }, $dashboard;
- }
- elsif ($Object->isa('RT::User')) {
- push @{ $dashboards{personal} }, $dashboard;
- }
- elsif ($Object->isa('RT::Group')) {
- push @{ $dashboards{group}{$Object->Name} }, $dashboard;
- }
+my $dashboards = RT::Dashboards->new($session{CurrentUser});
+$dashboards->LimitToObject($Object);
+while (my $dashboard = $dashboards->Next) {
+ if ($Object->isa('RT::System')) {
+ push @{ $dashboards{system} }, $dashboard;
+ }
+ elsif ($Object->isa('RT::User')) {
+ push @{ $dashboards{personal} }, $dashboard;
+ }
+ elsif ($Object->isa('RT::Group')) {
+ push @{ $dashboards{group}{$Object->Name} }, $dashboard;
}
}
return \%dashboards;
diff --git a/share/html/Dashboards/Elements/ShowDashboards b/share/html/Dashboards/Elements/ShowDashboards
index e04e149..37d4ca2 100644
--- a/share/html/Dashboards/Elements/ShowDashboards
+++ b/share/html/Dashboards/Elements/ShowDashboards
@@ -47,7 +47,7 @@
%# END BPS TAGGED BLOCK }}}
% foreach my $Object (@Objects) {
% my $Dashboards = RT::Dashboards->new($session{CurrentUser});
-% $Dashboards->LimitToPrivacy(join('-',ref($Object),$Object->Id));
+% $Dashboards->LimitToObject($Object);
% my $title;
% if (ref $Object eq 'RT::User' && $Object->Id == $session{CurrentUser}->Id) {
% $title = loc("My dashboards");
diff --git a/share/html/Dashboards/Modify.html b/share/html/Dashboards/Modify.html
index bba6edf..f8f7376 100644
--- a/share/html/Dashboards/Modify.html
+++ b/share/html/Dashboards/Modify.html
@@ -89,8 +89,7 @@ my $redirect_to ='/Dashboards/Modify.html';
use RT::Dashboard;
my $Dashboard = RT::Dashboard->new($session{'CurrentUser'});
-my $method = $Create ? 'ObjectsForCreating' : 'ObjectsForModifying';
-my @privacies = $Dashboard->$method;
+my @privacies = grep { $Create ? $Dashboard->CurrentUserCanCreate($_) : $Dashboard->CurrentUserCanModify($_) } $Dashboard->_PrivacyObjects;
Abort(loc("Permission Denied")) if @privacies == 0;
@@ -101,7 +100,7 @@ else {
if ($id eq 'new') {
$tried_create = 1;
- my ($val, $msg) = $Dashboard->Save(
+ my ($val, $msg) = $Dashboard->Create(
Name => $ARGS{'Name'},
Privacy => $ARGS{'Privacy'},
);
@@ -133,16 +132,15 @@ else {
}
if (!$Create && !$tried_create && $id && $ARGS{'Save'}) {
- my ($ok, $msg) = $Dashboard->Update(Privacy => $ARGS{'Privacy'},
- Name => $ARGS{'Name'});
-
- if ($ok) {
- push @results, loc("Dashboard [_1] updated", $Dashboard->Name);
+ my ( $ok, $msg );
+ if ( $Dashboard->Privacy ne $ARGS{'Privacy'} ) {
+ ( $ok, $msg ) = $Dashboard->SetPrivacy($ARGS{'Privacy'}) ;
+ push @results, $msg;
}
- else {
- push @results, loc("Dashboard [_1] could not be updated: [_2]", $Dashboard->Name, $msg);
+ if ( $Dashboard->Name ne $ARGS{'Name'} ) {
+ ( $ok, $msg ) = $Dashboard->SetName($ARGS{'Name'});
+ push @results, $msg;
}
-
}
diff --git a/share/html/Dashboards/Queries.html b/share/html/Dashboards/Queries.html
index d84b860..c348947 100644
--- a/share/html/Dashboards/Queries.html
+++ b/share/html/Dashboards/Queries.html
@@ -252,7 +252,7 @@ for my $pane (sort keys %pane_name) {
panes => $panes,
);
- my ($ok, $msg) = $Dashboard->Update(Panes => $panes);
+ my ($ok, $msg) = $Dashboard->SetPanes($panes);
if ($ok) {
push @results, loc("Dashboard updated");
diff --git a/t/web/dashboards-basics.t b/t/web/dashboards-basics.t
index c3533a3..40dc79e 100644
--- a/t/web/dashboards-basics.t
+++ b/t/web/dashboards-basics.t
@@ -68,7 +68,7 @@ $m->form_name('ModifyDashboard');
$m->field("Name" => 'different dashboard');
$m->content_lacks('Delete', "Delete button hidden because we are creating");
$m->click_button(value => 'Create');
-$m->content_contains("Saved dashboard different dashboard");
+$m->content_like(qr/Dashboard \d+ created/);
$user_obj->PrincipalObj->GrantRight(Right => 'SeeOwnDashboard', Object => $RT::System);
$m->get($url."Dashboards/index.html");
$m->follow_link_ok({ text => 'different dashboard'});
@@ -194,13 +194,13 @@ $m->content_contains('Delete', "Delete button shows because we have DeleteOwnDas
$m->form_name('ModifyDashboard');
$m->click_button(name => 'Delete');
-$m->content_contains("Deleted dashboard");
+$m->content_like(qr/Dashboard \d+ deleted/);
$m->get("/Dashboards/Modify.html?id=$id");
$m->content_lacks("different dashboard", "dashboard was deleted");
-$m->content_contains("Failed to load dashboard $id");
+$m->text_contains("Couldn't load dashboard $id");
-$m->warning_like(qr/Failed to load dashboard.*Couldn't find row/, "the dashboard was deleted");
+$m->warning_like(qr/Couldn't load dashboard.*Couldn't find row/, "the dashboard was deleted");
$user_obj->PrincipalObj->GrantRight(Right => "SuperUser", Object => $RT::System);
@@ -225,7 +225,7 @@ $m->field("Privacy" => 'RT::System-1');
$m->content_lacks('Delete', "Delete button hidden because we are creating");
$m->click_button(value => 'Create');
$m->content_lacks("No permission to create dashboards");
-$m->content_contains("Saved dashboard system dashboard");
+$m->content_like(qr/Dashboard \d+ created/);
$m->follow_link_ok({id => 'page-content'});
@@ -262,7 +262,7 @@ my ($bad_id) = $personal =~ /^search-(\d+)/;
for my $page (qw/Modify Queries Render Subscription/) {
$m->get("/Dashboards/$page.html?id=$bad_id");
- $m->content_like(qr/Couldn.+t load dashboard $bad_id: Invalid object type/);
- $m->warning_like(qr/Couldn't load dashboard $bad_id: Invalid object type/);
+ $m->text_like(qr/Couldn't load dashboard $bad_id/);
+ $m->warning_like(qr/Couldn't load dashboard $bad_id/);
}
diff --git a/t/web/dashboards-deleted-saved-search.t b/t/web/dashboards-deleted-saved-search.t
index cb96aca..c82f7de 100644
--- a/t/web/dashboards-deleted-saved-search.t
+++ b/t/web/dashboards-deleted-saved-search.t
@@ -37,7 +37,7 @@ $m->submit_form(
fields => { Name => 'bar' },
);
-$m->content_contains('Saved dashboard bar', 'dashboard saved' );
+$m->content_like(qr/Dashboard \d+ created/, 'dashboard created' );
my $dashboard_queries_link = $m->find_link( text_regex => qr/Content/ );
my ( $dashboard_id ) = $dashboard_queries_link->url =~ /id=(\d+)/;
diff --git a/t/web/dashboards-groups.t b/t/web/dashboards-groups.t
index 9f1c37d..05b4711 100644
--- a/t/web/dashboards-groups.t
+++ b/t/web/dashboards-groups.t
@@ -80,7 +80,7 @@ $m->field("Name" => 'inner dashboard');
$m->field("Privacy" => "RT::Group-" . $inner_group->Id);
$m->click_button(value => 'Create');
$m->content_lacks("Permission Denied", "we now have SeeGroupDashboard");
-$m->content_contains("Saved dashboard inner dashboard");
+$m->content_like(qr/Dashboard \d+ created/);
$m->content_lacks('Delete', "Delete button hidden because we lack DeleteDashboard");
my $dashboard = RT::Dashboard->new($currentuser);
@@ -98,7 +98,6 @@ $m->content_contains("inner dashboard", "we now have SeeGroupDashboard right");
$m->content_lacks("Permission Denied");
$m->content_contains('Subscription', "Subscription link not hidden because we have SubscribeDashboard");
-
$m->get_ok("/Dashboards/index.html");
$m->content_contains("inner dashboard", "We can see the inner dashboard from the UI");
diff --git a/t/web/dashboards-in-menu.t b/t/web/dashboards-in-menu.t
index 3126d55..d5acf9f 100644
--- a/t/web/dashboards-in-menu.t
+++ b/t/web/dashboards-in-menu.t
@@ -5,13 +5,13 @@ use RT::Test tests => 31;
my ($baseurl, $m) = RT::Test->started_ok;
my $system_foo = RT::Dashboard->new($RT::SystemUser);
-$system_foo->Save(
+$system_foo->Create(
Name => 'system foo',
Privacy => 'RT::System-' . $RT::System->id,
);
my $system_bar = RT::Dashboard->new($RT::SystemUser);
-$system_bar->Save(
+$system_bar->Create(
Name => 'system bar',
Privacy => 'RT::System-' . $RT::System->id,
);
@@ -42,9 +42,9 @@ diag "setting in admin users";
my $root = RT::CurrentUser->new( $RT::SystemUser );
ok( $root->Load('root') );
my $self_foo = RT::Dashboard->new($root);
-$self_foo->Save( Name => 'self foo', Privacy => 'RT::User-' . $root->id );
+$self_foo->Create( Name => 'self foo', Privacy => 'RT::User-' . $root->id );
my $self_bar = RT::Dashboard->new($root);
-$self_bar->Save( Name => 'self bar', Privacy => 'RT::User-' . $root->id );
+$self_bar->Create( Name => 'self bar', Privacy => 'RT::User-' . $root->id );
ok( !$m->find_link( text => 'self foo' ), 'no self foo link' );
$m->get_ok( $baseurl."/Admin/Users/DashboardsInMenu.html?id=" . $root->id);
diff --git a/t/web/dashboards-search-cache.t b/t/web/dashboards-search-cache.t
index 18989d5..6c4fc9f 100644
--- a/t/web/dashboards-search-cache.t
+++ b/t/web/dashboards-search-cache.t
@@ -25,7 +25,7 @@ $m->get_ok("$url/Dashboards/Modify.html?Create=1");
$m->form_name('ModifyDashboard');
$m->field('Name' => 'inner dashboard');
$m->click_button(value => 'Create');
-$m->text_contains('Saved dashboard inner dashboard');
+$m->text_like(qr/Dashboard \d+ created/);
my ($inner_id) = $m->content =~ /name="id" value="(\d+)"/;
ok($inner_id, "got an ID, $inner_id");
@@ -35,7 +35,7 @@ $m->get_ok("$url/Dashboards/Modify.html?Create=1");
$m->form_name('ModifyDashboard');
$m->field('Name' => 'cachey dashboard');
$m->click_button(value => 'Create');
-$m->text_contains('Saved dashboard cachey dashboard');
+$m->text_like(qr/Dashboard \d+ created/);
my ($dashboard_id) = $m->content =~ /name="id" value="(\d+)"/;
ok($dashboard_id, "got an ID, $dashboard_id");
@@ -91,7 +91,7 @@ $m->get_ok("/Dashboards/Modify.html?id=$inner_id");
$m->form_name('ModifyDashboard');
$m->field('Name' => 'recursive dashboard');
$m->click_button(value => 'Save Changes');
-$m->text_contains('Dashboard recursive dashboard updated');
+$m->text_contains('Name changed');
# check subscription page again
$m->get_ok("/Dashboards/Subscription.html?id=$dashboard_id");
-----------------------------------------------------------------------
More information about the rt-commit
mailing list