[Rt-commit] rtir branch, 5.0/search-selection, created. 4.0.1rc1-176-g69c13c0e
Craig Kaiser
craig at bestpractical.com
Thu Jun 4 13:42:28 EDT 2020
The branch, 5.0/search-selection has been created
at 69c13c0efb60db81240ebf742be1159354fcf7a5 (commit)
- Log -----------------------------------------------------------------
commit 4f976bfd0890c8024d87ffbcf1c144646c15bc33
Author: craig kaiser <craig at bestpractical.com>
Date: Thu Jun 4 13:16:08 2020 -0400
Use new search selection interface for editing RTIR home page
diff --git a/html/RTIR/Prefs/Home.html b/html/RTIR/Prefs/Home.html
index 7081f86d..900726fa 100644
--- a/html/RTIR/Prefs/Home.html
+++ b/html/RTIR/Prefs/Home.html
@@ -49,79 +49,211 @@
<& /Elements/Tabs &>
<& /Elements/ListActions, actions => \@results &>
-% foreach my $pane ( @panes ) {
-<&|/Widgets/TitleBox,
- title => loc('RTIR at a glance') .': '. loc( $pane->{Name} ),
- bodyclass => ""
-&>
-<& /Widgets/SelectionBox:show, self => $pane &>
+<form method="post" name="UpdateSearches" class="mx-auto max-width-lg">
+ <& /Widgets/SearchSelection,
+ pane_name => \%pane_name,
+ sections => \@sections,
+ selected => \%selected,
+ filters => \@filters,
+ &>
+ <input type="hidden" name="dashboard_id" value="MyRT">
+ <& /Elements/Submit, Name => "UpdateSearches", Label => loc('Save') &>
+</form>
+
+<&|/Widgets/TitleBox, title => loc('Options'), bodyclass => "", class => "mx-auto max-width-lg" &>
+<form method="post" action="MyRT.html">
+ <div class="form-row">
+ <div class="label col-auto">
+ <&|/l&>Rows per box</&>:
+ </div>
+ <div class="col-auto">
+ <input class="form-control" type="text" name="SummaryRows" value="<% $ARGS{SummaryRows} %>" />
+ </div>
+ <div class="col-auto">
+ <input type="submit" class="button form-control btn btn-primary" name="UpdateSummaryRows" value="<% loc('Save') %>" />
+ </div>
+ </div>
+</form>
</&>
-% }
-<%INIT>
-# XXX: copy&past of the similar RT's page
+<%INIT>
my @results;
+my $title = loc("Customize").' '.loc("RT at a glance");
my $user = $session{'CurrentUser'}->UserObj;
-unless (exists $session{'my_rtir_portlets'}) {
- my ($d_portlets) = RT::System->new($session{'CurrentUser'})->Attributes->Named('RTIR_HomepageSettings');
- $session{'my_rtir_portlets'} = $user->Preferences('RTIR_HomepageSettings', $d_portlets->Content);
+if ($ARGS{Reset}) {
+ next unless $user->Preferences('RTIR_HomepageSettings');
+ my ($ok, $msg) = $user->DeletePreferences('RTIR_HomepageSettings');
+ push @results, $msg unless $ok;
+
+ push @results, loc('Preferences saved.') unless @results;
}
-my $portlets = $session{'my_rtir_portlets'};
-
-my %seen;
-
-my @items;
-foreach my $comp ( grep !$seen{$_}++, (RT->Config->Get('RTIR_HomepageComponents')) ) {
- my $desc;
- my $obj = $m->fetch_comp($comp);
- $desc = $obj->attr_if_exists('Description') if $obj;
- unless( $desc ) {
- $desc = $comp;
- $desc =~ s/^.*\///;
- }
- push @items, ["component-$comp", $desc];
+
+my $portlets = $user->Preferences('RTIR_HomepageSettings');
+unless ($portlets) {
+ my ($defaults) = RT::System->new($session{'CurrentUser'})->Attributes->Named('RTIR_HomepageSettings');
+ $portlets = $defaults ? $defaults->Content : {};
}
+my @sections;
+my %item_for;
+
+my @components = map { type => "component", name => $_, label => loc($_) }, @{RT->Config->Get('RTIR_HomepageComponents')};
+
+$item_for{ $_->{type} }{ $_->{name} } = $_ for @components;
+
+push @sections, {
+ id => 'components',
+ label => loc("Components"),
+ items => \@components,
+};
+
my $sys = RT::System->new($session{'CurrentUser'});
my @objs = ($sys);
-push @objs, RT::SavedSearch->new( $session{'CurrentUser'} )->_PrivacyObjects
+push @objs, RT::SavedSearch->new( $session{CurrentUser} )->ObjectsForLoading
if $session{'CurrentUser'}->HasRight( Right => 'LoadSavedSearch',
- Object => $RT::System );
+ Object => $RT::System );
for my $object (@objs) {
+ my @items;
+ my $object_id = ref($object) . '-' . $object->Id;
+ $object_id = 'system' if $object eq $sys;
+
for ($m->comp("/Search/Elements/SearchesForObject", Object => $object)) {
my ($desc, $loc_desc, $search) = @$_;
- my $SearchType = $search->Content->{'SearchType'} || 'Ticket';
+
+ my $SearchType = 'Ticket';
+ if ((ref($search->Content)||'') eq 'HASH') {
+ $SearchType = $search->Content->{'SearchType'}
+ if $search->Content->{'SearchType'};
+ }
+ else {
+ $RT::Logger->debug("Search ".$search->id." ($desc) appears to have no Content");
+ }
+
+ my $item;
if ($object eq $sys && $SearchType eq 'Ticket') {
- push @items, ["system-$desc", $loc_desc];
+ $item = { type => 'system', name => $desc, label => $loc_desc };
}
else {
- my $oid = ref($object).'-'.$object->Id.'-SavedSearch-'.$search->Id;
- my $type = ($SearchType eq 'Ticket')
- ? 'Saved Search' : $SearchType; # loc
- push @items, ["saved-$oid", loc($type).": $loc_desc"];
+ my $oid = $object_id.'-SavedSearch-'.$search->Id;
+ $item = { type => 'saved', name => $oid, search_type => $SearchType, label => $loc_desc };
}
+
+ $item_for{ $item->{type} }{ $item->{name} } = $item;
+ push @items, $item;
}
+
+ my $label = $object eq $sys ? loc('System')
+ : $object->isa('RT::Group') ? $object->Label
+ : $object->Name;
+
+ push @sections, {
+ id => $object_id,
+ label => $label,
+ items => [ sort { lc($a->{label}) cmp lc($b->{label}) } @items ],
+ };
}
-my @panes = $m->comp(
- '/Admin/Elements/ConfigureMyRT',
- panes => ['body', 'sidebar'],
- Action => 'Home.html',
- items => \@items,
- current_portlets => $portlets,
- OnSave => sub {
- my ( $conf, $pane ) = @_;
- my ($ok, $msg) = $user->SetPreferences( 'RTIR_HomepageSettings', $conf );
- push @results, $ok ? loc('Preferences saved for [_1].', $pane) : $msg;
- delete $session{'my_rtir_portlets'};
+my %selected;
+for my $pane (keys %$portlets) {
+ my @items;
+
+ for my $saved (@{ $portlets->{$pane} }) {
+ my $item = $item_for{ $saved->{type} }{ $saved->{name} };
+ if ($item) {
+ push @items, $item;
+ }
+ else {
+ push @results, loc('Unable to find [_1] [_2]', $saved->{type}, $saved->{name});
+ }
}
+
+ $selected{$pane} = \@items;
+}
+
+my %pane_name = (
+ 'body' => loc('Body'),
+ 'sidebar' => loc('Sidebar'),
+);
+
+my @filters = (
+ [ 'component' => loc('Components') ],
+ [ 'ticket' => loc('Tickets') ],
+ [ 'chart' => loc('Charts') ],
);
-$m->comp ('/Widgets/SelectionBox:process', %ARGS, self => $_ ) for @panes;
+$m->callback(
+ CallbackName => 'Default',
+ pane_name => \%pane_name,
+ sections => \@sections,
+ selected => \%selected,
+ filters => \@filters,
+);
+
+if ($ARGS{UpdateSearches}) {
+ my $data = {
+ "panes" => {
+ "body" => [],
+ "sidebar" => []
+ }
+ };
+
+ foreach my $arg (qw{ body sidebar }) {
+ my $pane = $arg;
+ my $values = $ARGS{$pane};
+
+ next unless $values;
+
+ # force value to an arrayref so we can handle both single and multiple members of each pane.
+ $values = [$values] unless ref $values;
+
+ foreach my $value ( @{$values} ) {
+ $value =~ m/^(\w+)-(.+)$/i;
+ my $type = $1;
+ my $name = $2;
+ push @{ $data->{panes}->{$pane} }, { type => $type, name => $name };
+ }
+ }
+
+ my ( $ok, $msg );
+ my $user = $session{CurrentUser};
-MaybeRedirectForResults( Actions => \@results );
+ if ( my $user_id = $ARGS{user_id} ) {
+ my $UserObj = RT::User->new( $session{'CurrentUser'} );
+ ( $ok, $msg ) = $UserObj->Load($user_id);
+ ( $ok, $msg ) = $UserObj->SetPreferences( 'RTIR_HomepageSettings', $data->{panes} );
+ if ( $ok ) {
+ push @results, loc("Preferences saved.");
+ }
+ else {
+ push @results, loc("Could not save preferences.");
+ }
+ } elsif ( $ARGS{is_global} ) {
+ my $sys = RT::System->new( $session{'CurrentUser'} );
+ my ($default_portlets) = $sys->Attributes->Named('RTIR_HomepageSettings');
+ ( $ok, $msg ) = $default_portlets->SetContent( $data->{panes} );
+ if ( $ok ) {
+ push @results, loc("Preferences saved.");
+ }
+ else {
+ push @results, loc("Could not save preferences.");
+ }
+ } else {
+ ( $ok, $msg ) = $user->SetPreferences( 'RTIR_HomepageSettings', $data->{panes} );
+ if ( $ok ) {
+ push @results, loc("Preferences saved.");
+ }
+ else {
+ push @results, loc("Could not save preferences.");
+ }
+ }
+
+ MaybeRedirectForResults(
+ Actions => \@results,
+ Path => "/RTIR/Prefs/Home.html",
+ );
+}
</%INIT>
diff --git a/html/RTIR/index.html b/html/RTIR/index.html
index 77909674..0b855c80 100644
--- a/html/RTIR/index.html
+++ b/html/RTIR/index.html
@@ -51,14 +51,11 @@
<& /Elements/MyRT, Portlets => $portlets &>
<%INIT>
-# XXX: this should be automated!!!
-unless ( exists $session{'my_rtir_portlets'} ) {
- my ($d_portlets) = RT::System->new( $session{'CurrentUser'} )->Attributes->Named('RTIR_HomepageSettings');
- $session{'my_rtir_portlets'} = $session{'CurrentUser'}->UserObj->Preferences(
- 'RTIR_HomepageSettings', $d_portlets->Content
- );
+my $portlets = $session{'CurrentUser'}->Preferences('RTIR_HomepageSettings');
+unless ($portlets) {
+ my ($defaults) = RT::System->new($session{'CurrentUser'})->Attributes->Named('RTIR_HomepageSettings');
+ $portlets = $defaults ? $defaults->Content : {};
}
-my $portlets = $session{'my_rtir_portlets'};
if ( defined $q && length $q ) {
commit 69c13c0efb60db81240ebf742be1159354fcf7a5
Author: craig kaiser <craig at bestpractical.com>
Date: Thu Jun 4 13:30:20 2020 -0400
Add tests for new search selection UI for RTIR homepage
diff --git a/t/web/custom_frontpage.t b/t/web/custom_frontpage.t
new file mode 100644
index 00000000..437c8e37
--- /dev/null
+++ b/t/web/custom_frontpage.t
@@ -0,0 +1,138 @@
+use strict;
+use warnings;
+
+use RT::IR::Test tests => undef;
+my ($baseurl, $m) = RT::IR::Test->started_ok;
+
+my $url = $m->rt_base_url;
+
+my $user_obj = RT::User->new(RT->SystemUser);
+my ($ret, $msg) = $user_obj->LoadOrCreateByEmail('customer at example.com');
+ok($ret, 'ACL test user creation');
+$user_obj->SetName('customer');
+$user_obj->SetPrivileged(1);
+($ret, $msg) = $user_obj->SetPassword('customer');
+$user_obj->PrincipalObj->GrantRight(Right => 'LoadSavedSearch');
+$user_obj->PrincipalObj->GrantRight(Right => 'EditSavedSearches');
+$user_obj->PrincipalObj->GrantRight(Right => 'CreateSavedSearch');
+$user_obj->PrincipalObj->GrantRight(Right => 'ModifySelf');
+
+ok $m->login( customer => 'customer' ), "logged in";
+
+$m->get ( $url."Search/Build.html");
+
+#create a saved search
+$m->form_name ('BuildQuery');
+
+$m->field ( "ValueOfAttachment" => 'stupid');
+$m->field ( "SavedSearchDescription" => 'stupid tickets');
+$m->click_button (name => 'SavedSearchSave');
+
+$m->get ( $url.'RTIR/Prefs/Home.html' );
+$m->content_contains('stupid tickets', 'saved search listed in rt at a glance items');
+
+ok $m->login('root', 'password', logout => 1), 'we did log in as root';
+
+my $args = {
+ UpdateSearches => "Save",
+ dashboard_id => "RTIR_HomepageSettings",
+ body => [],
+ sidebar => [],
+};
+
+# remove all portlets from the body pane except 'newest unowned tickets'
+push(
+ @{$args->{body}},
+ ( "system-Unowned Tickets", )
+);
+
+my $res = $m->post(
+ $url . 'RTIR/Prefs/Home.html',
+ $args,
+);
+
+is( $res->code, 200, "remove all portlets from body except 'newest unowned tickets'" );
+like( $m->uri, qr/results=[A-Za-z0-9]{32}/, 'URL redirected for results' );
+$m->content_contains( 'Preferences saved' );
+
+$m->get( $url."RTIR/" );
+$m->content_contains( 'newest unowned tickets', "'newest unowned tickets' is present" );
+$m->content_lacks( 'highest priority tickets', "'highest priority tickets' is not present" );
+$m->content_lacks( 'Bookmarked Tickets<span class="results-count">', "'Bookmarked Tickets' is not present" ); # 'Bookmarked Tickets' also shows up in the nav, so we need to be more specific
+$m->content_lacks( 'Quick ticket creation', "'Quick ticket creation' is not present" );
+
+# add back the previously removed portlets
+push(
+ @{$args->{body}},
+ ( "system-My Tickets", "system-Bookmarked Tickets", "component-QuickCreate" )
+);
+
+push(
+ @{$args->{sidebar}},
+ ( "component-MyReminders", "component-QueueList", "component-Dashboards", "component-RefreshHomepage", )
+);
+
+$res = $m->post(
+ $url . 'RTIR/Prefs/Home.html',
+ $args,
+);
+
+is( $res->code, 200, 'add back previously removed portlets' );
+like( $m->uri, qr/results=[A-Za-z0-9]{32}/, 'URL redirected for results' );
+$m->content_contains( 'Preferences saved' );
+
+$m->get( $url."RTIR/" );
+$m->content_contains( 'newest unowned tickets', "'newest unowned tickets' is present" );
+$m->content_contains( 'highest priority tickets', "'highest priority tickets' is present" );
+$m->content_contains( 'Bookmarked Tickets<span class="results-count">', "'Bookmarked Tickets' is present" );
+$m->content_contains( 'Quick ticket creation', "'Quick ticket creation' is present" );
+
+#create a saved search with special chars
+$m->get( $url . "Search/Build.html" );
+$m->form_name('BuildQuery');
+$m->field( "ValueOfAttachment" => 'stupid' );
+$m->field( "SavedSearchDescription" => 'special chars [test] [_1] ~[_1~]' );
+$m->click_button( name => 'SavedSearchSave' );
+my ($name) = $m->content =~ /value="(RT::User-\d+-SavedSearch-\d+)"/;
+ok( $name, 'saved search name' );
+$m->get( $url . 'RTIR/Prefs/Home.html' );
+$m->content_contains( 'special chars [test] [_1] ~[_1~]',
+ 'saved search listed in rt at a glance items' );
+
+# add saved search to body
+push(
+ @{$args->{body}},
+ ( "saved-" . $name )
+);
+
+$res = $m->post(
+ $url . 'RTIR/Prefs/Home.html',
+ $args,
+);
+
+is( $res->code, 200, 'add saved search to body' );
+like( $m->uri, qr/results=[A-Za-z0-9]{32}/, 'URL redirected for results' );
+$m->content_contains( 'Preferences saved' );
+
+$m->get( $url."RTIR/" );
+$m->content_like( qr/special chars \[test\] \d+ \[_1\]/,
+ 'special chars in titlebox' );
+
+# Edit a system saved search to contain "[more]"
+{
+ my $search = RT::Attribute->new( RT->SystemUser );
+ $search->LoadByNameAndObject( Name => 'Search - My Tickets', Object => RT->System );
+ my ($id, $desc) = ($search->id, RT->SystemUser->loc($search->Description, '"N"'));
+ ok $id, 'loaded search attribute';
+
+ $m->get( $url."RTIR/" );
+ $m->follow_link_ok({ url_regex => qr"Prefs/Search\.html\?name=.+?Attribute-$id" }, 'Edit link');
+ $m->content_contains($desc, "found description: $desc");
+
+ ok +($search->SetDescription( $search->Description . " [more]" ));
+
+ $m->get_ok($m->uri); # "reload_ok"
+ $m->content_contains($desc . " [more]", "found description: $desc");
+}
+
+done_testing;
-----------------------------------------------------------------------
More information about the rt-commit
mailing list