RT#39913: Conexiant API [distinguish all rows previously imported from truly empty...
[freeside.git] / rt / t / web / dashboards-basics.t
1 use strict;
2 use warnings;
3
4 use RT::Test tests => 122;
5 my ($baseurl, $m) = RT::Test->started_ok;
6
7 my $url = $m->rt_base_url;
8
9 my $user_obj = RT::User->new(RT->SystemUser);
10 my ($ret, $msg) = $user_obj->LoadOrCreateByEmail('customer@example.com');
11 ok($ret, 'ACL test user creation');
12 $user_obj->SetName('customer');
13 $user_obj->SetPrivileged(1);
14 ($ret, $msg) = $user_obj->SetPassword('customer');
15 $user_obj->PrincipalObj->GrantRight(Right => 'ModifySelf');
16 my $currentuser = RT::CurrentUser->new($user_obj);
17
18 my $onlooker = RT::User->new(RT->SystemUser);
19 ($ret, $msg) = $onlooker->LoadOrCreateByEmail('onlooker@example.com');
20 ok($ret, 'ACL test user creation');
21 $onlooker->SetName('onlooker');
22 $onlooker->SetPrivileged(1);
23 ($ret, $msg) = $onlooker->SetPassword('onlooker');
24
25 my $queue = RT::Queue->new(RT->SystemUser);
26 $queue->Create(Name => 'SearchQueue'.$$);
27
28 for my $user ($user_obj, $onlooker) {
29     $user->PrincipalObj->GrantRight(Right => 'ModifySelf');
30     for my $right (qw/SeeQueue ShowTicket OwnTicket/) {
31         $user->PrincipalObj->GrantRight(Right => $right, Object => $queue);
32     }
33 }
34
35 ok $m->login(customer => 'customer'), "logged in";
36
37 $m->get_ok($url."Dashboards/index.html");
38 $m->content_lacks('<a href="/Dashboards/Modify.html?Create=1">New</a>', 
39                   "No 'new dashboard' link because we have no CreateOwnDashboard");
40
41 $m->no_warnings_ok;
42
43 $m->get_ok($url."Dashboards/Modify.html?Create=1");
44 $m->content_contains("Permission Denied");
45 $m->content_lacks("Save Changes");
46
47 $m->warning_like(qr/Permission Denied/, "got a permission denied warning");
48
49 $user_obj->PrincipalObj->GrantRight(Right => 'ModifyOwnDashboard', Object => $RT::System);
50
51 # Modify itself is no longer good enough, you need Create
52 $m->get_ok($url."Dashboards/Modify.html?Create=1");
53 $m->content_contains("Permission Denied");
54 $m->content_lacks("Save Changes");
55
56 $m->warning_like(qr/Permission Denied/, "got a permission denied warning");
57
58 $user_obj->PrincipalObj->GrantRight(Right => 'CreateOwnDashboard', Object => $RT::System);
59
60 $m->get_ok($url."Dashboards/Modify.html?Create=1");
61 $m->content_lacks("Permission Denied");
62 $m->content_contains("Create");
63
64 $m->get_ok($url."Dashboards/index.html");
65 $m->content_contains("New", "'New' link because we now have ModifyOwnDashboard");
66 $m->follow_link_ok({ id => 'home-dashboard_create'});
67 $m->form_name('ModifyDashboard');
68 $m->field("Name" => 'different dashboard');
69 $m->content_lacks('Delete', "Delete button hidden because we are creating");
70 $m->click_button(value => 'Create');
71 $m->content_contains("Saved dashboard different dashboard");
72 $user_obj->PrincipalObj->GrantRight(Right => 'SeeOwnDashboard', Object => $RT::System);
73 $m->get($url."Dashboards/index.html");
74 $m->follow_link_ok({ text => 'different dashboard'});
75 $m->content_lacks("Permission Denied", "we now have SeeOwnDashboard");
76 $m->content_lacks('Delete', "Delete button hidden because we lack DeleteOwnDashboard");
77
78 $m->get_ok($url."Dashboards/index.html");
79 $m->content_contains("different dashboard", "we now have SeeOwnDashboard");
80 $m->content_lacks("Permission Denied");
81
82 $m->follow_link_ok({text => "different dashboard"});
83 $m->content_contains("Basics");
84 $m->content_contains("Content");
85 $m->content_lacks("Subscription", "we don't have the SubscribeDashboard right");
86
87 $m->follow_link_ok({text => "Basics"});
88 $m->content_contains("Modify the dashboard different dashboard");
89
90 $m->follow_link_ok({text => "Content"});
91 $m->content_contains("Modify the content of dashboard different dashboard");
92 my $form = $m->form_name('Dashboard-Searches-body');
93 my @input = $form->find_input('Searches-body-Available');
94 my ($unowned) =
95   map { ( $_->possible_values )[1] }
96   grep { ( $_->value_names )[1] =~ /Saved Search: Unowned Tickets/ } @input;
97 $form->value('Searches-body-Available' => $unowned );
98 $m->click_button(name => 'add');
99 $m->content_contains("Dashboard updated");
100
101 my $dashboard = RT::Dashboard->new($currentuser);
102 my ($id) = $m->content =~ /name="id" value="(\d+)"/;
103 ok($id, "got an ID, $id");
104 $dashboard->LoadById($id);
105 is($dashboard->Name, "different dashboard");
106
107 is($dashboard->Privacy, 'RT::User-' . $user_obj->Id, "correct privacy");
108 is($dashboard->PossibleHiddenSearches, 0, "all searches are visible");
109
110 my @searches = $dashboard->Searches;
111 is(@searches, 1, "one saved search in the dashboard");
112 like($searches[0]->Name, qr/newest unowned tickets/, "correct search name");
113
114 $form = $m->form_name('Dashboard-Searches-body');
115 @input = $form->find_input('Searches-body-Available');
116 my ($my_tickets) =
117   map { ( $_->possible_values )[1] }
118   grep { ( $_->value_names )[1] =~ /Saved Search: My Tickets/ } @input;
119 $form->value('Searches-body-Available' => $my_tickets );
120 $m->click_button(name => 'add');
121 $m->content_contains("Dashboard updated");
122
123 RT::Record->FlushCache if RT::Record->can('FlushCache');
124 $dashboard = RT::Dashboard->new($currentuser);
125 $dashboard->LoadById($id);
126
127 @searches = $dashboard->Searches;
128 is(@searches, 2, "two saved searches in the dashboard");
129 like($searches[0]->Name, qr/newest unowned tickets/, "correct existing search name");
130 like($searches[1]->Name, qr/highest priority tickets I own/, "correct new search name");
131
132 my $ticket = RT::Ticket->new(RT->SystemUser);
133 $ticket->Create(
134     Queue     => $queue->Id,
135     Requestor => [ $user_obj->Name ],
136     Owner     => $user_obj,
137     Subject   => 'dashboard test',
138 );
139
140 $m->follow_link_ok({id => 'page-show'});
141 $m->content_contains("50 highest priority tickets I own");
142 $m->content_contains("50 newest unowned tickets");
143 $m->content_unlike( qr/Bookmarked Tickets.*Bookmarked Tickets/s,
144     'only dashboard queries show up' );
145 $m->content_contains("dashboard test", "ticket subject");
146
147 $m->get_ok("/Dashboards/$id/This fragment left intentionally blank");
148 $m->content_contains("50 highest priority tickets I own");
149 $m->content_contains("50 newest unowned tickets");
150 $m->content_unlike( qr/Bookmarked Tickets.*Bookmarked Tickets/s,
151     'only dashboard queries show up' );
152 $m->content_contains("dashboard test", "ticket subject");
153
154 $m->get_ok("/Dashboards/Subscription.html?id=$id");
155 $m->form_name('SubscribeDashboard');
156 $m->click_button(name => 'Save');
157 $m->content_contains("Permission Denied");
158 $m->warning_like(qr/Unable to subscribe to dashboard.*Permission Denied/, "got a permission denied warning when trying to subscribe to a dashboard");
159
160 $user_obj->Attributes->RedoSearch;
161 is($user_obj->Attributes->Named('Subscription'), 0, "no subscriptions");
162
163 $user_obj->PrincipalObj->GrantRight(Right => 'SubscribeDashboard', Object => $RT::System);
164
165 $m->get_ok("/Dashboards/Modify.html?id=$id");
166 $m->follow_link_ok({text => "Subscription"});
167 $m->content_contains("Subscribe to dashboard different dashboard");
168 $m->content_contains("Unowned Tickets");
169 $m->content_contains("My Tickets");
170 $m->content_unlike( qr/Bookmarked Tickets.*Bookmarked Tickets/s,
171     'only dashboard queries show up' );
172
173 $m->form_name('SubscribeDashboard');
174 $m->click_button(name => 'Save');
175 $m->content_lacks("Permission Denied");
176 $m->content_contains("Subscribed to dashboard different dashboard");
177
178 $user_obj->Attributes->RedoSearch;
179 is($user_obj->Attributes->Named('Subscription'), 1, "we have a subscription");
180
181 $m->get_ok("/Dashboards/Modify.html?id=$id");
182 $m->follow_link_ok({text => "Subscription"});
183 $m->content_contains("Modify the subscription to dashboard different dashboard");
184
185 $m->get_ok("/Dashboards/Modify.html?id=$id&Delete=1");
186 $m->content_contains("Permission Denied", "unable to delete dashboard because we lack DeleteOwnDashboard");
187
188 $m->warning_like(qr/Couldn't delete dashboard.*Permission Denied/, "got a permission denied warning when trying to delete the dashboard");
189
190 $user_obj->PrincipalObj->GrantRight(Right => 'DeleteOwnDashboard', Object => $RT::System);
191
192 $m->get_ok("/Dashboards/Modify.html?id=$id");
193 $m->content_contains('Delete', "Delete button shows because we have DeleteOwnDashboard");
194
195 $m->form_name('ModifyDashboard');
196 $m->click_button(name => 'Delete');
197 $m->content_contains("Deleted dashboard");
198
199 $m->get("/Dashboards/Modify.html?id=$id");
200 $m->content_lacks("different dashboard", "dashboard was deleted");
201 $m->content_contains("Failed to load dashboard $id");
202
203 $m->warning_like(qr/Failed to load dashboard.*Couldn't find row/, "the dashboard was deleted");
204
205 $user_obj->PrincipalObj->GrantRight(Right => "SuperUser", Object => $RT::System);
206
207 # now test that we warn about searches others can't see
208 # first create a personal saved search...
209 $m->get_ok($url."Search/Build.html");
210 $m->follow_link_ok({text => 'Advanced'});
211 $m->form_with_fields('Query');
212 $m->field(Query => "id > 0");
213 $m->submit;
214
215 $m->form_with_fields('SavedSearchDescription');
216 $m->field(SavedSearchDescription => "personal search");
217 $m->click_button(name => "SavedSearchSave");
218
219 # then the system-wide dashboard
220 $m->get_ok($url."Dashboards/Modify.html?Create=1");
221
222 $m->form_name('ModifyDashboard');
223 $m->field("Name" => 'system dashboard');
224 $m->field("Privacy" => 'RT::System-1');
225 $m->content_lacks('Delete', "Delete button hidden because we are creating");
226 $m->click_button(value => 'Create');
227 $m->content_lacks("No permission to create dashboards");
228 $m->content_contains("Saved dashboard system dashboard");
229
230 $m->follow_link_ok({id => 'page-content'});
231
232 $form = $m->form_name('Dashboard-Searches-body');
233 @input = $form->find_input('Searches-body-Available');
234 my ($personal) =
235   map { ( $_->possible_values )[1] }
236   grep { ( $_->value_names )[1] =~ /Saved Search: personal search/ } @input;
237 $form->value('Searches-body-Available' => $personal );
238 $m->click_button(name => 'add');
239 $m->content_contains("Dashboard updated");
240
241 $m->content_contains("The following queries may not be visible to all users who can see this dashboard.");
242
243 $m->follow_link_ok({id => 'page-show'});
244 $m->content_contains("personal search", "saved search shows up");
245 $m->content_contains("dashboard test", "matched ticket shows up");
246
247 # make sure the onlooker can't see the search...
248 $onlooker->PrincipalObj->GrantRight(Right => 'SeeDashboard', Object => $RT::System);
249
250 my $omech = RT::Test::Web->new;
251 ok $omech->login(onlooker => 'onlooker'), "logged in";
252 $omech->get_ok("/Dashboards");
253
254 $omech->follow_link_ok({text => 'system dashboard'});
255 $omech->content_lacks("personal search", "saved search doesn't show up");
256 $omech->content_lacks("dashboard test", "matched ticket doesn't show up");
257
258 $omech->warning_like(qr/User .* tried to load container user /, "can't see other users' personal searches");
259
260 # make sure that navigating to dashboard pages with bad IDs throws an error
261 my ($bad_id) = $personal =~ /^search-(\d+)/;
262
263 for my $page (qw/Modify Queries Render Subscription/) {
264     $m->get("/Dashboards/$page.html?id=$bad_id");
265     $m->content_like(qr/Couldn.+t load dashboard $bad_id: Invalid object type/);
266     $m->warning_like(qr/Couldn't load dashboard $bad_id: Invalid object type/);
267 }
268