[Rt-commit] rt branch, 5.0/self-service-homepage-dashboard, repushed
Dianne Skoll
dianne at bestpractical.com
Thu Sep 17 09:51:14 EDT 2020
The branch 5.0/self-service-homepage-dashboard was deleted and repushed:
was f15260de877e1b92dd8fa49609ad3c654d344440
now 72a7e4285b755f361b98e6947335c5f2eec95609
1: 302c1daaeb ! 1: e8ba9cb0ac Add ability to serve a custom dashboard as the SelfService home page.
@@ -1,37 +1,75 @@
Author: Dianne Skoll <dianne at bestpractical.com>
Add ability to serve a custom dashboard as the SelfService home page.
-
- You can enable the self-service home page dashboard in RT_SiteConfig.pm with:
-
- Set( $SelfServiceUseDashboard, 1);
-
- Note that you also need to give permissions for Everyone to see system
- dashboards.
-
- This code leverages the existing Dashboard infrastructure, but
- adds tweaks to save the self-service home page as singleton entry
- in the Attributes table, with the name "Selfservicedashboard"
-
- I made as few changes to the core Dashboards code as possible to get
- this to work; most of the work is done by RT::Dashboard::SelfService, a
- derived class from RT::Dashboard that overrides just enough of the
- database code to change the way the dashboard is loaded and stored.
-
- I did have to make some minor changes to the core dashboard files
- /Dashboards/Queries.html and lib/RT/Interface/Web.pm to get
- them to operate properly with the new class.
-
- /Dashboards/Queries.html is re-used almost as-is; an extra boolean
- parameter "self_service_dashboard" indicates that we're working on the
- self-service home page dashboard rather than a normal dashboard.
-
- For rendering the self-service page, we first verify that the
- self-service dashboard option is enabled, that the dashboard exists,
- and that it can be loaded. If all those conditions are met, we pass
- it to /Dashboards/Render.html which has similar minor modifications
- as /Dashboards/Queries.html to handle the special-case of the self-service
- page.
+
+diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
+--- a/etc/RT_Config.pm.in
++++ b/etc/RT_Config.pm.in
+@@
+ =back
+
+
+-
+-
+ =head2 Ticket search
+
+ =over 4
+@@
+
+ Set($SelfServiceShowGroupTickets, 0);
+
++=item C<$SelfServicePageComponents>
++
++C<$SelfServicePageComponents> is an arrayref of allowed components on
++the SelfService page, if you have set $SelfServiceUseDashboard to true.
++If this is not set at all, then $HomepageComponents is used instead.
++
++=cut
++
++Set(
++ $SelfServicePageComponents,
++ [
++ qw(QuickCreate QueueList MyAdminQueues MySupportQueues MyReminders RefreshHomepage Dashboards SavedSearches FindUser MyAssets FindAsset FindGroup SelfServiceTopArticles ) # loc_qw
++ ]
++);
++
++=item C<$SelfServiceUseDashboard>
++
++C<$SelfServiceUseDashboard> is a flag indicating whether or not to use
++a dashboard for the Self Service home page. If it is set to false,
++then the normal Open Tickets / Closed Tickets menu is shown rather
++than a dashboard.
++
++=cut
++
++Set($SelfServiceUseDashboard, 0);
++
++=item C<$SelfServiceArticleClass>
++
++C<$SelfServiceArticleClass> limits the articles shown to self-service
++users to the specified class.
++
++=cut
++
++Set($SelfServiceArticleClass, "SelfService");
++
+ =back
+
+ =head2 Articles
+
+diff --git a/lib/RT/Config.pm b/lib/RT/Config.pm
+--- a/lib/RT/Config.pm
++++ b/lib/RT/Config.pm
+@@
+ SearchResultsAutoRedirect => {
+ Widget => '/Widgets/Form/Boolean',
+ },
++ SelfServiceUseDashboard => {
++ Widget => '/Widgets/Form/Boolean',
++ },
+ ShowBccHeader => {
+ Widget => '/Widgets/Form/Boolean',
+ },
diff --git a/lib/RT/Dashboard/SelfService.pm b/lib/RT/Dashboard/SelfService.pm
new file mode 100644
@@ -120,7 +158,7 @@
+
+sub PostLoadValidate {
+ my $self = shift;
-+ return (0, "Invalid object type") unless $self->{'Attribute'}->Name eq 'Selfservicedashboard';
++ return (0, "Invalid object type") unless $self->{'Attribute'}->Name eq 'SelfServiceDashboard';
+ return 1;
+}
+
@@ -130,7 +168,7 @@
+ my $args = shift;
+
+ return $object->AddAttribute(
-+ 'Name' => 'Selfservicedashboard',
++ 'Name' => 'SelfServiceDashboard',
+ 'Description' => $args->{'Name'},
+ 'Content' => {Panes => $args->{'Panes'}},
+ );
@@ -199,10 +237,17 @@
$queues->UnLimit;
@@
+ } elsif ( $queue_id ) {
$top->child( new => title => loc('New ticket'), path => '/SelfService/Create.html?Queue=' . $queue_id );
}
- my $tickets = $top->child( tickets => title => loc('Tickets'), path => '/SelfService/' );
+- my $tickets = $top->child( tickets => title => loc('Tickets'), path => '/SelfService/' );
- $tickets->child( open => title => loc('Open tickets'), path => '/SelfService/' );
++
++ my $menu_label = loc('Tickets');
++ if (RT->Config->Get('SelfServiceUseDashboard')) {
++ $menu_label = loc('Self-Service');
++ }
++ my $tickets = $top->child( tickets => title => $menu_label, path => '/SelfService/' );
+ $tickets->child( open => title => loc('Open tickets'), path => '/SelfService/Open.html' );
$tickets->child( closed => title => loc('Closed tickets'), path => '/SelfService/Closed.html' );
@@ -283,7 +328,7 @@
+# need to get an ID to reuse the rest of the dashboard code.
+my $attr = RT::Attribute->new(RT->SystemUser);
+my ($ok, $msg) = $attr->LoadByNameAndObject(Object => $RT::System,
-+ Name => 'Selfservicedashboard');
++ Name => 'SelfServiceDashboard');
+if (!$ok) {
+ my $blank_dashboard = {
+ Panes => {
@@ -309,6 +354,112 @@
+
+</%INIT>
+diff --git a/share/html/Articles/Article/Edit.html b/share/html/Articles/Article/Edit.html
+--- a/share/html/Articles/Article/Edit.html
++++ b/share/html/Articles/Article/Edit.html
+@@
+ $ARGS{Disabled} = $ARGS{Enabled} ? 0 : 1;
+ }
+
++my $sortorder_ok = 1;
++if ($ARGS{SortOrder}) {
++ if ($ARGS{SortOrder} !~ /^-?\d+$/) {
++ push @results, (0, loc('Sort Order must be an integer'));
++ $sortorder_ok = 0;
++ }
++}
++
+ if ( !$id ) {
+ $title = loc('Create a new article');
+ foreach my $arg ( sort keys %ARGS ) {
+@@
+ split( /\s+/, $ARGS{'new-RefersTo'} );
+ }
+
+- my %cfs = ProcessObjectCustomFieldUpdatesForCreate(
+- ARGSRef => \%ARGS,
+- ContextObject => $ClassObj,
+- );
+-
+- my $msg;
+- ( $id, $msg ) = $ArticleObj->Create(
+- Summary => $ARGS{'Summary'},
+- Name => $ARGS{'Name'},
+- Class => $ARGS{'Class'},
+- Topics => $ARGS{'Topics'},
+- Disabled => $ARGS{'Disabled'},
+- %create_args,
+- %cfs
+- );
+- push( @results, $msg );
+- if ($id) {
++ my %cfs;
++ if ($sortorder_ok) {
++ %cfs = ProcessObjectCustomFieldUpdatesForCreate(
++ ARGSRef => \%ARGS,
++ ContextObject => $ClassObj,
++ );
+
++ my $msg;
++ ( $id, $msg ) = $ArticleObj->Create(
++ Summary => $ARGS{'Summary'},
++ SortOrder => $ARGS{'SortOrder'},
++ Name => $ARGS{'Name'},
++ Class => $ARGS{'Class'},
++ Topics => $ARGS{'Topics'},
++ Disabled => $ARGS{'Disabled'},
++ %create_args,
++ %cfs
++ );
++ push( @results, $msg );
++ } else {
++ $id = 0;
++ }
+
++ if ($id) {
+ $ArticleObj->Load($id);
+
+ $title = loc( 'Modify article #[_1]', $ArticleObj->Id );
+@@
+
+ my @attribs = qw(Name Summary Class Disabled);
+
+- @results = UpdateRecordObject(
+- AttributesRef => \@attribs,
+- Object => $ArticleObj,
+- ARGSRef => \%ARGS
+- );
++ if ($sortorder_ok) {
++ @results = UpdateRecordObject(
++ AttributesRef => \@attribs,
++ Object => $ArticleObj,
++ ARGSRef => \%ARGS
++ );
++ }
+
+ my @cf_results = ProcessObjectCustomFieldUpdates(
+ Object => $ArticleObj,
+
+diff --git a/share/html/Dashboards/Elements/ShowPortlet/component b/share/html/Dashboards/Elements/ShowPortlet/component
+--- a/share/html/Dashboards/Elements/ShowPortlet/component
++++ b/share/html/Dashboards/Elements/ShowPortlet/component
+@@
+ my $full_path = $Portlet->{path};
+ (my $path = $full_path) =~ s{^/Elements/}{};
+
+-my $allowed = grep { $_ eq $path } @{RT->Config->Get('HomepageComponents')};
++my $allowed;
++
++if ($m->request_path =~ m{/SelfService}) {
++ $allowed = grep { $_ eq $path } @{RT->Config->Get('SelfServicePageComponents') || RT->Config->Get('HomepageComponents')};
++} else {
++ $allowed = grep { $_ eq $path } @{RT->Config->Get('HomepageComponents')};
++}
++
+ </%init>
+ % if (!$allowed) {
+ % $m->out( $m->interp->apply_escapes( loc("Invalid portlet [_1]", $path), "h" ) );
+
diff --git a/share/html/Dashboards/Queries.html b/share/html/Dashboards/Queries.html
--- a/share/html/Dashboards/Queries.html
+++ b/share/html/Dashboards/Queries.html
@@ -343,6 +494,18 @@
my @sections;
my %item_for;
+
+-my @components = map { type => "component", name => $_, label => loc($_) }, @{RT->Config->Get('HomepageComponents')};
++my @components;
++
++if ($self_service_dashboard) {
++ @components = map { type => "component", name => $_, label => loc($_) }, @{RT->Config->Get('SelfServicePageComponents') || RT->Config->Get('HomepageComponents')};
++} else {
++ @components = map { type => "component", name => $_, label => loc($_) }, @{RT->Config->Get('HomepageComponents')};
++}
+
+ $item_for{ $_->{type} }{ $_->{name} } = $_ for @components;
+
@@
if ( $ARGS{UpdateSearches} ) {
@@ -427,6 +590,172 @@
+$self_service_dashboard => 0
</%ARGS>
+
+diff --git a/share/html/Elements/SelfServiceTopArticles b/share/html/Elements/SelfServiceTopArticles
+new file mode 100644
+--- /dev/null
++++ b/share/html/Elements/SelfServiceTopArticles
+@@
++%# BEGIN BPS TAGGED BLOCK {{{
++%#
++%# COPYRIGHT:
++%#
++%# This software is Copyright (c) 1996-2020 Best Practical Solutions, LLC
++%# <sales at bestpractical.com>
++%#
++%# (Except where explicitly superseded by other copyright notices)
++%#
++%#
++%# LICENSE:
++%#
++%# This work is made available to you under the terms of Version 2 of
++%# the GNU General Public License. A copy of that license should have
++%# been provided with this software, but in any event can be snarfed
++%# from www.gnu.org.
++%#
++%# This work is distributed in the hope that it will be useful, but
++%# WITHOUT ANY WARRANTY; without even the implied warranty of
++%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++%# General Public License for more details.
++%#
++%# You should have received a copy of the GNU General Public License
++%# along with this program; if not, write to the Free Software
++%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++%# 02110-1301 or visit their web page on the internet at
++%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
++%#
++%#
++%# CONTRIBUTION SUBMISSION POLICY:
++%#
++%# (The following paragraph is not intended to limit the rights granted
++%# to you to modify and distribute this software under the terms of
++%# the GNU General Public License and is only of importance to you if
++%# you choose to contribute your changes and enhancements to the
++%# community by submitting them to Best Practical Solutions, LLC.)
++%#
++%# By intentionally submitting any modifications, corrections or
++%# derivatives to this work, or any other work intended for use with
++%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
++%# you are the copyright holder for those contributions and you grant
++%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
++%# royalty-free, perpetual, license to use, copy, create derivative
++%# works based on those contributions, and sublicense and distribute
++%# those contributions and any derivatives thereof.
++%#
++%# END BPS TAGGED BLOCK }}}
++
++<& /Elements/TopArticles,
++ title => $title,
++ display_path => 'SelfService/Article',
++ classname => (RT->Config->Get('SelfServiceArticleClass') || 'SelfService') &>
++
++<%ARGS>
++$title => undef
++</%ARGS>
+
+diff --git a/share/html/Elements/TopArticles b/share/html/Elements/TopArticles
+new file mode 100644
+--- /dev/null
++++ b/share/html/Elements/TopArticles
+@@
++%# BEGIN BPS TAGGED BLOCK {{{
++%#
++%# COPYRIGHT:
++%#
++%# This software is Copyright (c) 1996-2020 Best Practical Solutions, LLC
++%# <sales at bestpractical.com>
++%#
++%# (Except where explicitly superseded by other copyright notices)
++%#
++%#
++%# LICENSE:
++%#
++%# This work is made available to you under the terms of Version 2 of
++%# the GNU General Public License. A copy of that license should have
++%# been provided with this software, but in any event can be snarfed
++%# from www.gnu.org.
++%#
++%# This work is distributed in the hope that it will be useful, but
++%# WITHOUT ANY WARRANTY; without even the implied warranty of
++%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++%# General Public License for more details.
++%#
++%# You should have received a copy of the GNU General Public License
++%# along with this program; if not, write to the Free Software
++%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++%# 02110-1301 or visit their web page on the internet at
++%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
++%#
++%#
++%# CONTRIBUTION SUBMISSION POLICY:
++%#
++%# (The following paragraph is not intended to limit the rights granted
++%# to you to modify and distribute this software under the terms of
++%# the GNU General Public License and is only of importance to you if
++%# you choose to contribute your changes and enhancements to the
++%# community by submitting them to Best Practical Solutions, LLC.)
++%#
++%# By intentionally submitting any modifications, corrections or
++%# derivatives to this work, or any other work intended for use with
++%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
++%# you are the copyright holder for those contributions and you grant
++%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
++%# royalty-free, perpetual, license to use, copy, create derivative
++%# works based on those contributions, and sublicense and distribute
++%# those contributions and any derivatives thereof.
++%#
++%# END BPS TAGGED BLOCK }}}
++
++<&| /Widgets/TitleBox, title => $title &>
++
++% while (my $article = $articles->Next) {
++ <div class="form-row">
++ <span class="value col-auto">
++ <a href="<% RT->Config->Get('WebPath') %>/<% $display_path %>/Display.html?id=<%$article->Id%>"><%$article->Name || loc('(no name)')%>: <%$article->Summary%></a>
++ </span>
++ </div>
++% }
++</&>
++
++<%INIT>
++$title = loc('Articles');
++# Figure out which class of articles applies, if a classname was given
++my $class;
++
++if ($classname) {
++ $class = RT::Class->new( $session{'CurrentUser'} );
++ my ($ok, $msg) = $class->LoadByCols( Name => RT->Config->Get('SelfServiceArticleClass') );
++ if (!$ok || !$class->Id) {
++ # Could not find the class... bail?
++ return;
++ }
++}
++
++# Get the articles
++my $articles = RT::Articles->new( $session{'CurrentUser'} );
++
++$articles->RowsPerPage($rows);
++
++if ($class) {
++ $articles->Search(Class => $class->Id,
++ OrderBy => ['SortOrder', 'LastUpdated'],
++ Order => ['ASC', 'DESC' ]);
++} else {
++ $articles->Search(OrderBy => ['SortOrder', 'LastUpdated'],
++ Order => ['ASC', 'DESC' ]);
++}
++
++</%INIT>
++
++<%ARGS>
++$title => undef
++$rows => 10
++# Unfortunately, the directory hierarchy under SelfService
++# is just "Article" instead of "Articles/Article", so we have
++# to make to path for displaying an article a parameter.
++$display_path => 'Articles/Article'
++$classname => undef
++</%ARGS>
diff --git a/share/html/SelfService/Open.html b/share/html/SelfService/Open.html
new file mode 100644
@@ -537,7 +866,7 @@
+ # need to get an ID to reuse the rest of the dashboard code.
+ my $attr = RT::Attribute->new(RT->SystemUser);
+ my ($ok, $msg) = $attr->LoadByNameAndObject(Object => $RT::System,
-+ Name => 'Selfservicedashboard');
++ Name => 'SelfServiceDashboard');
+ if ($ok && $attr->Id) {
+ # Try to load the dashboard
+ my ($ok, $msg) = $Dashboard->LoadById($attr->Id);
2: a4ffc08fd3 < -: ------- Add a "SelfServiceTopArticles" portlet that can (only) be placed on the self-service dashboard.
3: f15260de87 ! 2: 72a7e4285b Add UI for editing and displaying the SortOrder associated with an article.
@@ -39,38 +39,9 @@
--- a/share/html/Articles/Article/Edit.html
+++ b/share/html/Articles/Article/Edit.html
@@
- split( /\s+/, $ARGS{'new-RefersTo'} );
- }
+ ContextObject => $ClassObj,
+ );
-- my %cfs = ProcessObjectCustomFieldUpdatesForCreate(
-- ARGSRef => \%ARGS,
-- ContextObject => $ClassObj,
-- );
-+ my $attempt_create = 1;
-+ if ($ARGS{SortOrder}) {
-+ if ($ARGS{SortOrder} !~ /^\d+$/) {
-+ push @results, (0, loc('Sort Order must be an integer'));
-+ $attempt_create = 0;
-+ $id = 0;
-+ }
-+ }
-+ if ($attempt_create) {
-+ my %cfs = ProcessObjectCustomFieldUpdatesForCreate(
-+ ARGSRef => \%ARGS,
-+ ContextObject => $ClassObj,
-+ );
-
-- my $msg;
-- ( $id, $msg ) = $ArticleObj->Create(
-- Summary => $ARGS{'Summary'},
-- Name => $ARGS{'Name'},
-- Class => $ARGS{'Class'},
-- Topics => $ARGS{'Topics'},
-- Disabled => $ARGS{'Disabled'},
-- %create_args,
-- %cfs
-- );
-- push( @results, $msg );
+ my $msg;
+ ( $id, $msg ) = $ArticleObj->Create(
+ Summary => $ARGS{'Summary'},
@@ -84,9 +55,10 @@
+ );
+ push( @results, $msg );
+ }
- if ($id) {
-
-
++ if ($id) {
+ my $msg;
+ ( $id, $msg ) = $ArticleObj->Create(
+ Summary => $ARGS{'Summary'},
@@
Why => loc("Unable to load article") );
}
@@ -94,28 +66,8 @@
- my @attribs = qw(Name Summary Class Disabled);
+ my @attribs = qw(Name Summary Class Disabled SortOrder);
-- @results = UpdateRecordObject(
-- AttributesRef => \@attribs,
-- Object => $ArticleObj,
-- ARGSRef => \%ARGS
-- );
-+ my $attempt_update = 1;
-+ if ($ARGS{SortOrder}) {
-+ if ($ARGS{SortOrder} !~ /^\d+$/) {
-+ push @results, (0, loc('Sort Order must be an integer'));
-+ $attempt_update = 0;
-+ }
-+ }
-+ if ($attempt_update) {
-+ @results = UpdateRecordObject(
-+ AttributesRef => \@attribs,
-+ Object => $ArticleObj,
-+ ARGSRef => \%ARGS
-+ );
-+ }
-
- my @cf_results = ProcessObjectCustomFieldUpdates(
- Object => $ArticleObj,
+ if ($sortorder_ok) {
+ @results = UpdateRecordObject(
diff --git a/share/html/Articles/Article/Elements/EditBasics b/share/html/Articles/Article/Elements/EditBasics
--- a/share/html/Articles/Article/Elements/EditBasics
More information about the rt-commit
mailing list