[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