Bug 16631: Institute library limits
This patch implements the library_limit concept for Report/Reports. Staff with the correct permission and superlibrarians can set libaries to which the report should be visible, if the system preference is enabled. Test plan: Confirm Initial Settings 1. Log in to Koha staff with username: koha and password: koha and set your library as Centerville if it's not already. 2. Visit the Saved reports page, and create and save three reports: Report 1, Report 2, and Report 3. The SQL query for each report doesn't matter, it should just be something valid. 3. Visit the Patrons page and create two staff users: StaffReportLimitUser and StaffNoLimitPermission. Set their home libraries to Centerville and Fairfield, respectively. 4. Under Allow access to the reports module, ensure both users have report management permissions for Create, Delete, Execute. Verify Patch Settings 5. Apply the patch and restart services. If using Koha Testing Docker (KTD): from the Koha repo directory, apply the patch (e.g. `git bz apply 16631`, or check out the patched branch with `git fetch && git checkout <branch>`), then run `ktd --shell` to enter the KTD shell and run `restart_all`. If the patched branch is already checked out, just enter the KTD shell and run `restart_all`. 6. Click Patrons in the top navbar to go to the Patrons page. 7. Search for "StaffReportLimitUser" in the patron search field. 8. Click StaffReportLimitUser to edit the patron record for this user. 9. Click More -> Set permissions to access the permissions screens for the StaffReportLimitUser. 10. In the row for Allow access to the reports module (reports), click Show details to view relevant permissions. 11. Confirm Manage Report Limits (manage_report_limits) is an option under Allow access to the reports module (reports). 12. Click the checkbox for Manage Report Limits to give this user this permission, and click Save. 13. Go to the System Preferences page (Click the More dropdown from the top nav bar, click Administration, click System Preferences). 14. Verify Reports is an option in the left side navigation bar, and that when clicked, you see the LimitReportsByBranch option. 15. Enable LimitReportsByBranch as described in step 14. 16. Go back to Saved reports as in Step 2. 17. Click Edit in the row menu for Report 1. 18. At the bottom of the Report 1 edit page, you should see a Library limitation select box. Verify the Library Limitation Settings are Persisted 19. Choose Centerville from the select box and click Update SQL. 20. Reload the page, or navigate away from the page and go back to the edit screen for Report 1. 21. Verify that Centerville is still in the Library limitation select box. 22. Add Fairfield in the Library limitation select box and click Update and run SQL. 23. Go back to editing Report 1. 24. Confirm the Library limitation select box has both Centerville and Fairfield selected. 25. Duplicate Report 1. 26. Confirm the Library limitation select box has both Centerville and Fairfield selected. 27. Click Save Report. 28. Confirm the Library limitation select box has both Centerville and Fairfield selected. Verify Reports are Limited Based on Library Limitations 29. Edit Report 1 to add Centerville in the Library limitation select box as the only choice and click Update SQL. 30. Edit Report 2 to add Fairfield in the Library limitation select box as the only choice and click Update SQL. 31. Report 3 should have no library limitations, so it should be visible to all libraries. 32. Go to Saved reports as in Step 2. 33. Confirm you do not see Report 2, and only Report 1 and Report 3. 34. In the upper right corner, click the drop down that says koha Centerville and click Set library to change your library. 35. Set your library to Fairfield. 36. Go to Saved reports as in Step 2. 37. Confirm you do not see Report 1, and only Report 2 and Report 3. Verify Disabling LimitReportsByBranch Preference Shows All Reports 38. Disable LimitReportsByBranch preference as in steps 13 to 15. 39. Go to Saved reports as in Step 2. 40. Confirm Report 1, Report 2, Report 3 are visible. 41. Click to edit Report 1 and verify there is no Library limitation select box. Verify Library limitations are Persisted When EnableFilteringReports is Toggled On or Off 42. Enable LimitReportsByBranch preference as in steps 13 to 15. 43. Ensure your library is set to Fairfield as in step 35. 44. Go to Saved reports as in Step 2. 45. Confirm Report 1 is not visible, and Report 2 and Report 3 are visible. 46. Click to edit Report 2 and verify the Library limitation select box is at the bottom of the page and that it has Fairfield selected. Verify the Show all reports option shows all reports for users with the Manage Report Limits permission and superlibrarians 47. Go to Saved reports as in Step 2 and in the Show reports dropdown, select Show all reports, and click Apply filter. 48. Confirm the page reloads with all reports visible: Report 1, Report 2, and Report 3 should be visible. 49. Select Show my branch reports from the dropdown and click Apply filter. 50. Confirm Report 1 is not visible, and Report 2 and Report 3 are visible (you should only see reports for your branch, Fairfield). Verify Report filters return the correct results 51. Go to Saved reports as in Step 2. 52. Verify you're viewing only your branch's reports in the filter. 53. Under Filter in the left sidebar, type "2" in the Keyword field and click Apply filter. 54. Confirm only Report 2 is visible. 55. Under Filter in the left sidebar, type "1" in the Keyword field and click Apply filter. 56. Confirm there are no reports visible, and the message "No saved reports match your criteria" is displayed. Verify Library Limits column is available to superlibrarians and users with the Manage Report Limits permission 57. Go to Saved reports as in Step 2. 58. Click the Columns dropdown and ensure that the Library limits column is available to select for display. Select it. 59. Confirm that the Library limits column is now visible in the list of reports and that it shows the correct library limitations for each report (e.g., Report 1 should show 1 library limitation and Centerville (CPL) on hover, Report 2 should show 1 library limitation and Fairfield (FFL) on hover, and Report 3 should show no limitation). Verify the Show all reports and Limit reports options are available to users that are not superlibrarian but have the Manage Report Limits permission 60. For the StaffReportLimitUser, add the permission for Manage Report Limits as described in steps 9 to 12. This will allow this user to see the Show all reports and Limit reports options. 61. Log out of the koha admin account. In the upper right corner, click the drop down that says koha Fairfield and click Log out. 62. Log in as StaffReportLimitUser, the previously created staff user with report permissions. 63. Confirm the staff user's library is Centerville in the upper right corner of the screen. 64. Go to Saved reports as in Step 2. 65. Confirm the filter option for Show reports is visible. 66. Click to edit Report 1. 67. Verify there is a Library limitation select box and that it can be interacted with. Verify the Show all reports and Limit reports options are not available to users that are not superlibrarian and do not have the Manage Report Limits permission 68. Log out of the StaffReportLimitUser account. 69. Log in as StaffNoLimitPermission, the previously created staff user without the Manage Report Limits permission. 70. Confirm the staff user's library is Fairfield in the upper right corner of the screen. 71. Go to Saved reports as in Step 2. 72. Click the Columns dropdown and ensure that the Library limits column is not in the list of available columns to select for display. 73. Confirm the filter option for Show reports is not available. 74. Click to edit Report 2. 75. Verify there is no Library limitation select box at the bottom of the page. [NEW] Verify library limits are auto-assigned and preserved correctly for non-permissioned users 76. Still logged in as StaffNoLimitPermission (Fairfield) from the previous section. Go to Saved reports and click New report. Enter "Limit Auto-Assign Test" as the report name and SELECT 1 as the SQL. Confirm there is no Library limitations select box visible on the form. Click Save. 77. Go back to Saved reports. Click Edit for Report 2 (limited to Fairfield). Add `-- test` as a trailing SQL comment and click Update SQL. This simulates a non-permissioned user saving an existing report that already has limits. 78. Go back to Saved reports. Click Edit for Report 3 (no library limits). Add `-- test` as a trailing SQL comment and click Update SQL. This simulates a non-permissioned user saving an existing report that has no limits. 79. Log out and log back in as StaffReportLimitUser (Centerville, has manage_report_limits). 80. Go to Saved reports. Use Show all reports if needed. Find "Limit Auto-Assign Test" and click Edit. Confirm the Library limitations select box shows Fairfield as the only selected library — the report was automatically limited to the creator's home library on create. 81. Find Report 2 and click Edit. Confirm the Library limitations select box still shows only Fairfield — the existing limit was preserved when the non-permissioned user saved the report. 82. Find Report 3 and click Edit. Confirm the Library limitations select box is empty — no limits were introduced when the non-permissioned user saved a report that had no existing limits. Verify limited reports are not visible to users without permissions 83. While still logged in as StaffReportLimitUser from step 79, copy the URL for the edit page of Report 1. 84. Log out of the StaffReportLimitUser account. 85. Log in as StaffNoLimitPermission, the user without the Manage Report Limits permission. 86. Paste the copied URL for the edit page of Report 1 into the browser and navigate to it. 87. Confirm that you see an error message that says "This report is restricted and cannot be accessed by the current user." and that the page does not load the report details. Run Unit Tests 88. Using Koha Testing Docker, in the Koha home directory run the command: ktd --shell to start the ktd shell. 89. Run command: reset_all to drop and recreate the koha database. 90. Run command prove t/db_dependent/Koha/Reports.t to run the test. 91. This file contains the unit tests for the reports_branches table and the new subtest `library limits enforcement on save and update`, which covers auto-assignment of branch limits on create and preservation of limits on update for non-permissioned users. 92. All tests should pass. 93. Run command prove t/Koha/Auth/Permissions.t 94. Permissions unit tests in this file were updated. 95. All tests should pass. 96. Run command prove t/db_dependent/Reports/Guided.t 97. The Guided report tests were updated to account for the new library limitations and permissions. 98. All tests should pass. Signoff 99. Sign off if the steps above check out. Sponsored-by: Ramapo Catskill Library System Co-authored-by: Casey Conlin <casey.conlin@gmail.com> Signed-off-by: Emmi Takkinen <emmi.takkinen@koha-suomi.fi> Signed-off-by: Andrew Fuerste Henry <andrew@bywatersolutions.com> Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This commit is contained in:
parent
60ee7fb527
commit
67a37a07c3
|
|
@ -881,7 +881,54 @@ sub get_saved_reports {
|
|||
|
||||
my $result = $dbh->selectall_arrayref( $query, { Slice => {} }, @args );
|
||||
|
||||
return $result;
|
||||
my $limit_pref_enabled = C4::Context->preference('LimitReportsByLibrary');
|
||||
|
||||
return $result unless $limit_pref_enabled;
|
||||
|
||||
# Preference enabled AND showing all: attach library limits metadata, but do not filter
|
||||
if ( $filter->{show_all_reports} ) {
|
||||
foreach my $report (@$result) {
|
||||
my $report_obj = Koha::Reports->find( $report->{id} );
|
||||
if ($report_obj) {
|
||||
my $limits_rs = $report_obj->get_library_limits;
|
||||
if ($limits_rs) {
|
||||
my @branches =
|
||||
map { { branchcode => $_->branchcode, branchname => $_->branchname } } $limits_rs->as_list;
|
||||
$report->{library_limits} = \@branches;
|
||||
} else {
|
||||
$report->{library_limits} = [];
|
||||
}
|
||||
} else {
|
||||
$report->{library_limits} = [];
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
# Preference enabled AND not showing all: apply library limits filtering
|
||||
my $limited_reports = Koha::Reports->search_with_library_limits( {}, {}, C4::Context::mybranch() );
|
||||
my %allowed = map { $_->{id} => 1 } @{ $limited_reports->unblessed };
|
||||
my @limited = grep { $allowed{ $_->{id} } } @$result;
|
||||
|
||||
# Attach library limits data for the filtered list
|
||||
foreach my $report (@limited) {
|
||||
my $report_obj = Koha::Reports->find( $report->{id} );
|
||||
if ($report_obj) {
|
||||
my $limits_rs = $report_obj->get_library_limits;
|
||||
if ($limits_rs) {
|
||||
my @branches =
|
||||
map { { branchcode => $_->branchcode, branchname => $_->branchname } } $limits_rs->as_list;
|
||||
$report->{library_limits} = \@branches;
|
||||
} else {
|
||||
$report->{library_limits} = [];
|
||||
}
|
||||
} else {
|
||||
$report->{library_limits} = [];
|
||||
}
|
||||
}
|
||||
|
||||
return \@limited;
|
||||
|
||||
}
|
||||
|
||||
=head2 get_column_type($column)
|
||||
|
|
|
|||
|
|
@ -321,6 +321,7 @@ sub _shared_preferences {
|
|||
autoMemberNum
|
||||
BorrowerRenewalPeriodBase
|
||||
EnableBorrowerFiles
|
||||
LimitReportsByLibrary
|
||||
EnhancedMessagingPreferences
|
||||
ExtendedPatronAttributes
|
||||
intranetreadinghistory
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ our $mapping = {
|
|||
'admin/preferences#logs' => '/logspreferences.html',
|
||||
'admin/preferences#opac' => '/opacpreferences.html',
|
||||
'admin/preferences#patrons' => '/patronspreferences.html',
|
||||
'admin/preferences#reports' => '/reportspreferences.html',
|
||||
'admin/preferences#searching' => '/searchingpreferences.html',
|
||||
'admin/preferences#serials' => '/serialspreferences.html',
|
||||
'admin/preferences#staff_interface' => '/staffclientpreferences.html',
|
||||
|
|
|
|||
|
|
@ -21,9 +21,15 @@ use Koha::Database;
|
|||
use Koha::Exceptions::Report;
|
||||
use Koha::Reports;
|
||||
|
||||
use Koha::Object;
|
||||
use Koha::Object::Limit::Library;
|
||||
use Koha::Patrons;
|
||||
use C4::Context;
|
||||
|
||||
use base qw(Koha::Object Koha::Object::Limit::Library);
|
||||
|
||||
#use Koha::DateUtils qw( dt_from_string output_pref );
|
||||
|
||||
use base qw(Koha::Object);
|
||||
#
|
||||
# FIXME We could only return an error code instead of the arrayref
|
||||
# Only 1 error is returned
|
||||
|
|
@ -347,4 +353,57 @@ sub _type {
|
|||
return 'SavedSql';
|
||||
}
|
||||
|
||||
=head3 _library_limits
|
||||
|
||||
Configurable library limits
|
||||
|
||||
=cut
|
||||
|
||||
sub _library_limits {
|
||||
return {
|
||||
class => "ReportsBranch",
|
||||
id => "report_id",
|
||||
library => "branchcode",
|
||||
};
|
||||
}
|
||||
|
||||
=head3 can_manage_limits
|
||||
|
||||
my $can = Koha::Report->can_manage_limits($patron);
|
||||
|
||||
Returns true if branch limiting is enabled and the patron can manage report limits (superlibrarian or permission reports => manage_report_limits).
|
||||
|
||||
=cut
|
||||
|
||||
sub can_manage_limits {
|
||||
my ( $class, $patron ) = @_;
|
||||
return C4::Context->preference('LimitReportsByLibrary')
|
||||
&& ( $patron->is_superlibrarian || $patron->has_permission( { reports => 'manage_report_limits' } ) );
|
||||
}
|
||||
|
||||
=head3 can_access
|
||||
|
||||
my $allowed = $report->can_access($patron);
|
||||
|
||||
Returns true if the patron may access this report considering branch limits, or if limits are disabled.
|
||||
|
||||
=cut
|
||||
|
||||
sub can_access {
|
||||
my ( $self, $patron ) = @_;
|
||||
|
||||
return 1 unless C4::Context->preference('LimitReportsByLibrary');
|
||||
|
||||
return 1 if __PACKAGE__->can_manage_limits($patron);
|
||||
|
||||
my $limits_rs = $self->get_library_limits;
|
||||
return 1 unless $limits_rs;
|
||||
|
||||
my $user_branch = C4::Context::mybranch();
|
||||
return 0 unless $user_branch;
|
||||
|
||||
my %allowed = map { $_->branchcode => 1 } $limits_rs->as_list;
|
||||
return $allowed{$user_branch} ? 1 : 0;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ use Koha::Logger;
|
|||
|
||||
use Koha::Report;
|
||||
|
||||
use base qw(Koha::Objects);
|
||||
use base qw(Koha::Objects Koha::Objects::Limit::Library);
|
||||
|
||||
=head1 NAME
|
||||
|
||||
|
|
|
|||
|
|
@ -1688,6 +1688,9 @@ modules:
|
|||
columnname: saved_results
|
||||
-
|
||||
columnname: update
|
||||
-
|
||||
columnname: library_limits
|
||||
is_hidden: 1
|
||||
-
|
||||
columnname: actions
|
||||
cannot_be_toggled: 1
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
use Modern::Perl;
|
||||
use Koha::Installer::Output qw(say_warning say_success say_info);
|
||||
|
||||
return {
|
||||
bug_number => "16631",
|
||||
description => "Add library limits to reports",
|
||||
up => sub {
|
||||
my ($args) = @_;
|
||||
my ( $dbh, $out ) = @$args{qw(dbh out)};
|
||||
|
||||
$dbh->do(
|
||||
q{CREATE TABLE IF NOT EXISTS `reports_branches` (
|
||||
`report_id` int(11) NOT NULL,
|
||||
`branchcode` varchar(10) NOT NULL,
|
||||
KEY `report_id` (`report_id`),
|
||||
KEY `branchcode` (`branchcode`),
|
||||
CONSTRAINT `reports_branches_ibfk_1` FOREIGN KEY (`report_id`) REFERENCES `saved_sql` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `reports_branches_ibfk_2` FOREIGN KEY (`branchcode`) REFERENCES `branches` (`branchcode`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci}
|
||||
);
|
||||
|
||||
say $out "Added new table 'reports_branches'";
|
||||
|
||||
$dbh->do(
|
||||
q{INSERT IGNORE INTO systempreferences
|
||||
(variable,value,options,explanation,type) VALUES ('LimitReportsByLibrary', '0', NULL, 'Enable ability for staff to limit report access based on home library.', 'YesNo')}
|
||||
);
|
||||
|
||||
say $out "Added new system preference 'LimitReportsByLibrary'";
|
||||
|
||||
$dbh->do(
|
||||
q{INSERT IGNORE INTO permissions
|
||||
(module_bit, code, description) VALUES (16, 'manage_report_limits', 'Manage report limits')}
|
||||
);
|
||||
|
||||
say $out "Added new permission 'manage_report_limits'";
|
||||
|
||||
},
|
||||
};
|
||||
|
|
@ -5957,6 +5957,19 @@ CREATE TABLE `saved_reports` (
|
|||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
DROP TABLE IF EXISTS `reports_branches`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8mb4 */;
|
||||
CREATE TABLE `reports_branches` (
|
||||
`report_id` int(11) NOT NULL,
|
||||
`branchcode` varchar(10) NOT NULL,
|
||||
KEY `report_id` (`report_id`),
|
||||
KEY `branchcode` (`branchcode`),
|
||||
CONSTRAINT `reports_branches_ibfk_1` FOREIGN KEY (`report_id`) REFERENCES `saved_sql` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `reports_branches_ibfk_2` FOREIGN KEY (`branchcode`) REFERENCES `branches` (`branchcode`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Table structure for table `saved_sql`
|
||||
--
|
||||
|
|
|
|||
|
|
@ -150,6 +150,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES
|
|||
(16, 'create_reports', 'Create SQL reports'),
|
||||
(16, 'delete_own_reports', 'Delete SQL reports you have created'),
|
||||
(16, 'delete_all_reports', 'Delete SQL reports created by anyone'),
|
||||
(16, 'manage_report_limits', 'Manage report limits'),
|
||||
(18, 'manage_courses', 'Add, edit and delete courses'),
|
||||
(18, 'add_reserves', 'Add course reserves'),
|
||||
(18, 'delete_reserves', 'Remove course reserves'),
|
||||
|
|
|
|||
|
|
@ -520,6 +520,9 @@
|
|||
[%- CASE 'execute_reports' -%]
|
||||
<span class="sub_permission execute_reports_subpermission"> Execute SQL reports </span>
|
||||
<span class="permissioncode">([% name | html %])</span>
|
||||
[%- CASE 'manage_report_limits' -%]
|
||||
<span class="sub_permission manage_report_limits_subpermission"> Manage Report Limits </span>
|
||||
<span class="permissioncode">([% name | html %])</span>
|
||||
[%- CASE 'add_reserves' -%]
|
||||
<span class="sub_permission add_reserves_subpermission"> Add course reserves </span>
|
||||
<span class="permissioncode">([% name | html %])</span>
|
||||
|
|
|
|||
|
|
@ -165,6 +165,17 @@
|
|||
</li>
|
||||
[% END %]
|
||||
|
||||
[% IF ( reports ) %]
|
||||
<li class="active">
|
||||
<a title="Reports" href="/cgi-bin/koha/admin/preferences.pl?tab=reports">Reports</a>
|
||||
[% PROCESS subtabs %]
|
||||
</li>
|
||||
[% ELSE %]
|
||||
<li>
|
||||
<a title="Reports" href="/cgi-bin/koha/admin/preferences.pl?tab=reports">Reports</a>
|
||||
</li>
|
||||
[% END %]
|
||||
|
||||
[% IF ( searching ) %]
|
||||
<li class="active">
|
||||
<a title="Searching" href="/cgi-bin/koha/admin/preferences.pl?tab=searching">Searching</a>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
Reports:
|
||||
Reports Access:
|
||||
-
|
||||
- pref: LimitReportsByLibrary
|
||||
choices:
|
||||
1: Enable
|
||||
0: Disable
|
||||
- "limiting report access based on staff home library."
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
[% USE AuthorisedValues %]
|
||||
[% USE KohaDates %]
|
||||
[% USE Koha %]
|
||||
[% USE Branches %]
|
||||
[% USE TablesSettings %]
|
||||
[% USE HtmlScrubber %]
|
||||
[% USE JSON.Escape %]
|
||||
|
|
@ -229,6 +230,15 @@
|
|||
|
||||
[% INCLUDE "reports-toolbar.inc" %]
|
||||
|
||||
[% IF access_denied %]
|
||||
<div>
|
||||
<h1>Access denied</h1>
|
||||
<p>This report is restricted and cannot be accessed by the current user.</p>
|
||||
[% IF id %]<p>Report ID: [% id | html %]</p>[% END %]
|
||||
<p><a class="btn btn-secondary" href="/cgi-bin/koha/reports/guided_reports.pl?op=list">Return to saved reports</a></p>
|
||||
</div>
|
||||
[% END %]
|
||||
|
||||
[% IF ( start ) %]
|
||||
<h1>Guided reports</h1>
|
||||
<p>Use the guided reports engine to create non standard reports. This feature aims to provide some middle ground between the built in canned reports and writing custom SQL reports.</p>
|
||||
|
|
@ -264,7 +274,7 @@
|
|||
<h1>Saved reports</h1>
|
||||
[% IF ( savedreports ) %]
|
||||
|
||||
[% IF ( filters.date || filters.author || filters.keyword ) %]
|
||||
[% IF ( filters.date || filters.author || filters.keyword || library_limit_active ) %]
|
||||
<p
|
||||
>Filtered by:
|
||||
<span class="filter">
|
||||
|
|
@ -277,6 +287,9 @@
|
|||
[% IF ( filters.keyword ) %]
|
||||
<span class="filter_keyword"><strong>Keyword:</strong> [% filters.keyword | html %]</span>
|
||||
[% END %]
|
||||
[% IF library_limit_active %]
|
||||
<span class="filter_show_all"><strong>Show reports:</strong> My library reports</span>
|
||||
[% END %]
|
||||
<a class="clear_filter" href="/cgi-bin/koha/reports/guided_reports.pl?op=list&clear_filters=1"><i class="fa fa-times" aria-hidden="true"></i> Clear</a>
|
||||
</span>
|
||||
</p>
|
||||
|
|
@ -337,6 +350,9 @@
|
|||
[% ELSE %]
|
||||
<th class="NoVisible">Update</th>
|
||||
[% END %]
|
||||
[% IF ( Koha.Preference('LimitReportsByLibrary') && ( CAN_user_superlibrarian || CAN_user_reports_manage_report_limits ) ) %]
|
||||
<th>Library limits</th>
|
||||
[% END %]
|
||||
<th class="no-sort no-export">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -404,6 +420,29 @@
|
|||
>
|
||||
[% END %]
|
||||
</td>
|
||||
[% IF ( Koha.Preference('LimitReportsByLibrary') && ( CAN_user_superlibrarian || CAN_user_reports_manage_report_limits ) ) %]
|
||||
<td>
|
||||
[% IF savedreport.library_limits && savedreport.library_limits.size > 0 %]
|
||||
[% library_str = "" %]
|
||||
[% FOREACH library IN savedreport.library_limits %]
|
||||
[%- IF loop.first -%]
|
||||
[% library_str = library.branchname _ " (" _ library.branchcode _ ")" %]
|
||||
[% ELSE %]
|
||||
[% library_str = library_str _ "\n" _ library.branchname _ " (" _ library.branchcode _ ")" %]
|
||||
[% END %]
|
||||
[% END %]
|
||||
<span class="library_limitation" data-bs-toggle="tooltip" title="[% library_str | html %]">
|
||||
[% IF savedreport.library_limits.size > 1 %]
|
||||
<span>[% savedreport.library_limits.size | html %] library limitations</span>
|
||||
[% ELSE %]
|
||||
<span>[% savedreport.library_limits.size | html %] library limitation</span>
|
||||
[% END %]
|
||||
</span>
|
||||
[% ELSE %]
|
||||
<span>No limitation</span>
|
||||
[% END %]
|
||||
</td>
|
||||
[% END %]
|
||||
<td>
|
||||
<div class="btn-group dropup">
|
||||
[%# There should be no space between these two buttons, it would render badly %]
|
||||
|
|
@ -502,7 +541,7 @@
|
|||
|
||||
<a href="/cgi-bin/koha/reports/guided_reports.pl?op=add_form_sql" class="new btn btn-default"><i class="fa fa-plus" aria-hidden="true"></i> New SQL report</a>
|
||||
|
||||
<a href="/cgi-bin/koha/reports/guided_reports.pl?op=list&filter_set=1&filter_keywork=" class="deny btn btn-default"><i class="fa fa-fw fa-times" aria-hidden="true"></i> Cancel filter</a>
|
||||
<a href="/cgi-bin/koha/reports/guided_reports.pl?op=list&filter_set=1&clear_filters=1" class="deny btn btn-default"><i class="fa fa-fw fa-times" aria-hidden="true"></i> Cancel filter</a>
|
||||
[% END %]
|
||||
[% ELSE %]
|
||||
<h4>There are no saved reports. </h4>
|
||||
|
|
@ -898,6 +937,7 @@
|
|||
<input type="hidden" name="reportname" value="[% reportname | html %]" />
|
||||
<input type="hidden" name="group" value="[% group | html %]" />
|
||||
<input type="hidden" name="subgroup" value="[% subgroup | html %]" />
|
||||
<input type="hidden" name="branches" value="[% branches | html %]" />
|
||||
<input type="hidden" name="notes" value="[% notes | scrub_html type => 'note' | $raw %]" />
|
||||
<input type="hidden" name="cache_expiry" value="[% cache_expiry | html %]" />
|
||||
<input type="hidden" name="cache_expiry_units" value="[% cache_expiry_units | html %]" />
|
||||
|
|
@ -1496,6 +1536,19 @@
|
|||
</fieldset>
|
||||
<!-- /.rows -->
|
||||
|
||||
[% IF ( Koha.Preference('LimitReportsByLibrary') && ( CAN_user_superlibrarian || CAN_user_reports_manage_report_limits ) ) %]
|
||||
<fieldset class="rows">
|
||||
<legend>Library limitation:</legend>
|
||||
<div>
|
||||
<label for="library_limitation">Library limitation: </label>
|
||||
<select id="library_limitation" name="branches" multiple size="10">
|
||||
[% PROCESS options_for_libraries libraries => Branches.all( selected => report.get_library_limits, unfiltered => 1, do_not_select_my_library => 1 ) %]
|
||||
</select>
|
||||
<div class="hint">Limits the use of this report to the selected libraries.</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
[% END %]
|
||||
|
||||
<fieldset class="rows">
|
||||
<legend>SQL:</legend>
|
||||
<div style="margin:1em;">
|
||||
|
|
@ -1616,6 +1669,19 @@
|
|||
</fieldset>
|
||||
<!-- /.rows -->
|
||||
|
||||
[% IF ( Koha.Preference('LimitReportsByLibrary') && ( CAN_user_superlibrarian || CAN_user_reports_manage_report_limits ) ) %]
|
||||
<fieldset class="rows">
|
||||
<legend>Library limitation:</legend>
|
||||
<div
|
||||
><label for="library_limitation">Library limitation: </label>
|
||||
<select id="library_limitation" name="branches" multiple size="10">
|
||||
[% PROCESS options_for_libraries libraries => Branches.all( selected => report.get_library_limits, unfiltered => 1, do_not_select_my_library => 1 ) %]
|
||||
</select>
|
||||
<div class="hint">Limits the use of this report to the selected libraries.</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
[% END %]
|
||||
|
||||
<fieldset class="rows">
|
||||
<legend>SQL:</legend>
|
||||
[% PROCESS insert_runtime_parameter %]
|
||||
|
|
@ -1698,6 +1764,26 @@
|
|||
<label for="filter_keyword">Keyword:</label>
|
||||
<input type="text" id="filter_keyword" name="filter_keyword" value="[% filters.keyword | html %]" size="16" />
|
||||
</li>
|
||||
<li>
|
||||
[% IF Koha.Preference('LimitReportsByLibrary') %]
|
||||
[% IF ( CAN_user_superlibrarian || CAN_user_reports_manage_report_limits ) %]
|
||||
<label for="filter_show_all_reports">Show reports:</label>
|
||||
<select name="filter_show_all_reports" id="filter_show_all_reports">
|
||||
[% IF filters.show_all_reports == "1" %]
|
||||
<option value="1" selected="selected">Show all reports</option>
|
||||
[% ELSE %]
|
||||
<option value="1">Show all reports</option>
|
||||
[% END %]
|
||||
|
||||
[% IF filters.show_all_reports == 0 %]
|
||||
<option value="0" selected="selected">Show my library reports</option>
|
||||
[% ELSE %]
|
||||
<option value="0">Show my library reports</option>
|
||||
[% END %]
|
||||
</select>
|
||||
[% END %]
|
||||
[% END %]
|
||||
</li>
|
||||
</ol>
|
||||
</fieldset>
|
||||
<!-- /.brief -->
|
||||
|
|
@ -2279,7 +2365,22 @@
|
|||
let report_table_settings = { columns };
|
||||
$("#report_results").kohaTable({}, report_table_settings);
|
||||
}
|
||||
if (saved1) {
|
||||
[% IF (saved1) %]
|
||||
var table_settings = [% TablesSettings.GetTableSettings( 'reports', 'saved-sql', 'table_reports', 'json' ) | $raw %];
|
||||
[% UNLESS ( Koha.Preference('LimitReportsByLibrary') && ( CAN_user_superlibrarian || CAN_user_reports_manage_report_limits ) ) %]
|
||||
table_settings.columns = table_settings.columns.filter(function(col) {
|
||||
return col.columnname !== 'library_limits';
|
||||
});
|
||||
[% END %]
|
||||
|
||||
[% IF ( Koha.Preference('LimitReportsByLibrary') && ( CAN_user_superlibrarian || CAN_user_reports_manage_report_limits ) ) %]
|
||||
let library_limits_col_index = -1;
|
||||
table_settings.columns.forEach(function(col, index) {
|
||||
if (col.columnname === 'library_limits') {
|
||||
library_limits_col_index = index;
|
||||
}
|
||||
});
|
||||
[% END %]
|
||||
var rtable = $("#table_reports").kohaTable(
|
||||
{
|
||||
autoWidth: false,
|
||||
|
|
@ -2287,7 +2388,9 @@
|
|||
order: [[1, "asc"]],
|
||||
language: {
|
||||
zeroRecords: _("No matching reports found"),
|
||||
},
|
||||
}[% IF ( Koha.Preference('LimitReportsByLibrary') && ( CAN_user_superlibrarian || CAN_user_reports_manage_report_limits ) ) %],
|
||||
columnDefs: library_limits_col_index !== -1 ? [{ visible: false, targets: [library_limits_col_index] }] : []
|
||||
[% END %]
|
||||
},
|
||||
table_settings
|
||||
);
|
||||
|
|
@ -2346,7 +2449,7 @@
|
|||
$("#update_sql_label").html("");
|
||||
$("#update_sql .modal-body").html('<div id="loading"><img src="' + interface + "/" + theme + '/img/spinner-small.gif" alt="" /> ' + _("Loading") + "</div>");
|
||||
});
|
||||
}
|
||||
[% END %]
|
||||
|
||||
if (op == "showsql") {
|
||||
$("#sql").focus(function () {
|
||||
|
|
@ -2578,6 +2681,12 @@
|
|||
selectField.style.minWidth = "320px";
|
||||
$(selectField).select2();
|
||||
});
|
||||
|
||||
const libraryLimitSelectFields = document.querySelectorAll('#library_limitation');
|
||||
libraryLimitSelectFields.forEach((selectField) => {
|
||||
selectField.style.minWidth = '450px';
|
||||
$(selectField).select2();
|
||||
});
|
||||
});
|
||||
|
||||
$("#toggle_auto_links").on("click", function (e) {
|
||||
|
|
|
|||
|
|
@ -101,22 +101,49 @@ my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
|
|||
flagsrequired => { reports => $flagsrequired },
|
||||
}
|
||||
);
|
||||
my $session_id = $input->cookie('CGISESSID');
|
||||
my $session = $session_id ? get_session($session_id) : undef;
|
||||
|
||||
$template->param( templates => Koha::Notice::Templates->search( { module => 'report' } ) );
|
||||
my $session_id = $input->cookie('CGISESSID');
|
||||
my $session = $session_id ? get_session($session_id) : undef;
|
||||
my $logged_in_user = Koha::Patrons->find($borrowernumber);
|
||||
my $limit_reports_by_library = C4::Context->preference('LimitReportsByLibrary');
|
||||
my $can_manage_report_limits = $logged_in_user && Koha::Report->can_manage_limits($logged_in_user);
|
||||
|
||||
my $filter;
|
||||
if ( $input->param("filter_set") or $input->param('clear_filters') ) {
|
||||
$filter = {};
|
||||
$filter->{$_} = $input->param("filter_$_") foreach qw/date author keyword group subgroup/;
|
||||
$filter->{$_} = $input->param("filter_$_") foreach qw/date author keyword show_all_reports group subgroup/;
|
||||
|
||||
if ( $input->param('clear_filters') ) {
|
||||
$filter->{show_all_reports} = 1;
|
||||
}
|
||||
|
||||
$session->param( 'report_filter', $filter ) if $session;
|
||||
$template->param( 'filter_set' => 1 );
|
||||
} elsif ( $session and not $input->param('clear_filters') ) {
|
||||
$filter = $session->param('report_filter');
|
||||
}
|
||||
|
||||
my @errors = ();
|
||||
my @errors = ();
|
||||
my %OP_REQUIRES_REPORT = map { $_ => 1 } qw(show edit_form duplicate cud-update_sql cud-update_and_run_sql export run);
|
||||
my $report_id = $input->param('id');
|
||||
my $report;
|
||||
my $access_blocked;
|
||||
if ( $OP_REQUIRES_REPORT{$op} && $report_id ) {
|
||||
$report = Koha::Reports->find($report_id);
|
||||
if ($report) {
|
||||
unless ( $report->can_access($logged_in_user) ) {
|
||||
$template->param( access_denied => 1, denied_op => $op, id => $report_id );
|
||||
$access_blocked = 1;
|
||||
}
|
||||
} else {
|
||||
push @errors, { no_sql_for_id => $report_id };
|
||||
}
|
||||
}
|
||||
|
||||
if ($access_blocked) {
|
||||
output_html_with_http_headers $input, $cookie, $template->output;
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( !$op ) {
|
||||
$template->param( 'start' => 1 );
|
||||
|
||||
|
|
@ -146,151 +173,164 @@ if ( !$op ) {
|
|||
$op = 'list';
|
||||
|
||||
} elsif ( $op eq 'show' ) {
|
||||
|
||||
my $id = $input->param('id');
|
||||
my $report = Koha::Reports->find($id);
|
||||
$template->param(
|
||||
'id' => $id,
|
||||
'reportname' => $report->report_name,
|
||||
'notes' => $report->notes,
|
||||
'report_creator' => $report->borrowernumber,
|
||||
'sql' => $report->savedsql,
|
||||
'showsql' => 1,
|
||||
'mana_success' => scalar $input->param('mana_success'),
|
||||
'mana_id' => $report->{mana_id},
|
||||
'mana_comments' => $report->{comments}
|
||||
);
|
||||
if ($report) {
|
||||
$template->param(
|
||||
'id' => $report_id,
|
||||
'reportname' => $report->report_name,
|
||||
'notes' => $report->notes,
|
||||
'report_creator' => $report->borrowernumber,
|
||||
'sql' => $report->savedsql,
|
||||
'showsql' => 1,
|
||||
'mana_success' => scalar $input->param('mana_success'),
|
||||
'mana_id' => $report->{mana_id},
|
||||
'mana_comments' => $report->{comments}
|
||||
);
|
||||
}
|
||||
|
||||
} elsif ( $op eq 'edit_form' ) {
|
||||
my $id = $input->param('id');
|
||||
my $report = Koha::Reports->find($id);
|
||||
my $group = $report->report_group;
|
||||
my $subgroup = $report->report_subgroup;
|
||||
my $tables = get_tables();
|
||||
$template->param(
|
||||
'report_creator' => $report->borrowernumber,
|
||||
'sql' => $report->savedsql,
|
||||
'reportname' => $report->report_name,
|
||||
'groups_with_subgroups' => groups_with_subgroups( $group, $subgroup ),
|
||||
'notes' => $report->notes,
|
||||
'id' => $id,
|
||||
'cache_expiry' => $report->cache_expiry,
|
||||
'public' => $report->public,
|
||||
'usecache' => $usecache,
|
||||
'editsql' => 1,
|
||||
'mana_id' => $report->{mana_id},
|
||||
'mana_comments' => $report->{comments},
|
||||
'tables' => $tables
|
||||
);
|
||||
if ($report) {
|
||||
my $group = $report->report_group;
|
||||
my $subgroup = $report->report_subgroup;
|
||||
my $tables = get_tables();
|
||||
$template->param(
|
||||
'report_creator' => $report->borrowernumber,
|
||||
'sql' => $report->savedsql,
|
||||
'reportname' => $report->report_name,
|
||||
'groups_with_subgroups' => groups_with_subgroups( $group, $subgroup ),
|
||||
'notes' => $report->notes,
|
||||
'id' => $report_id,
|
||||
'cache_expiry' => $report->cache_expiry,
|
||||
'public' => $report->public,
|
||||
'usecache' => $usecache,
|
||||
'editsql' => 1,
|
||||
'mana_id' => $report->{mana_id},
|
||||
'mana_comments' => $report->{comments},
|
||||
'tables' => $tables,
|
||||
'report' => $report,
|
||||
);
|
||||
}
|
||||
|
||||
} elsif ( $op eq 'cud-update_sql' || $op eq 'cud-update_and_run_sql' ) {
|
||||
my $id = $input->param('id');
|
||||
my $sql = $input->param('sql');
|
||||
my $reportname = $input->param('reportname');
|
||||
my $group = $input->param('group');
|
||||
my $subgroup = $input->param('subgroup');
|
||||
my $notes = $input->param('notes');
|
||||
my $cache_expiry = $input->param('cache_expiry');
|
||||
my $cache_expiry_units = $input->param('cache_expiry_units');
|
||||
my $public = $input->param('public');
|
||||
my $save_anyway = $input->param('save_anyway');
|
||||
my @errors;
|
||||
my $tables = get_tables();
|
||||
if ($report) {
|
||||
my $id = $report_id;
|
||||
my $sql = $input->param('sql');
|
||||
my $reportname = $input->param('reportname');
|
||||
my $group = $input->param('group');
|
||||
my $subgroup = $input->param('subgroup');
|
||||
my $notes = $input->param('notes');
|
||||
my $cache_expiry = $input->param('cache_expiry');
|
||||
my $cache_expiry_units = $input->param('cache_expiry_units');
|
||||
my $public = $input->param('public');
|
||||
my $save_anyway = $input->param('save_anyway');
|
||||
my @errors;
|
||||
my $tables = get_tables();
|
||||
my @branches = grep { $_ ne q{} } $input->multi_param('branches');
|
||||
|
||||
# if we have the units, then we came from creating a report from SQL and thus need to handle converting units
|
||||
if ($cache_expiry_units) {
|
||||
if ( $cache_expiry_units eq "minutes" ) {
|
||||
$cache_expiry *= 60;
|
||||
} elsif ( $cache_expiry_units eq "hours" ) {
|
||||
$cache_expiry *= 3600; # 60 * 60
|
||||
} elsif ( $cache_expiry_units eq "days" ) {
|
||||
$cache_expiry *= 86400; # 60 * 60 * 24
|
||||
if ( $limit_reports_by_library && !$can_manage_report_limits ) {
|
||||
my $existing_limits = $report->get_library_limits;
|
||||
@branches = $existing_limits ? map { $_->branchcode } $existing_limits->as_list : ();
|
||||
}
|
||||
}
|
||||
|
||||
# check $cache_expiry isn't too large, Memcached::set requires it to be less than 30 days or it will be treated as if it were an absolute time stamp
|
||||
if ( $cache_expiry >= 2592000 ) {
|
||||
push @errors, { cache_expiry => $cache_expiry };
|
||||
}
|
||||
# if we have the units, then we came from creating a report from SQL and thus need to handle converting units
|
||||
if ($cache_expiry_units) {
|
||||
if ( $cache_expiry_units eq "minutes" ) {
|
||||
$cache_expiry *= 60;
|
||||
} elsif ( $cache_expiry_units eq "hours" ) {
|
||||
$cache_expiry *= 3600; # 60 * 60
|
||||
} elsif ( $cache_expiry_units eq "days" ) {
|
||||
$cache_expiry *= 86400; # 60 * 60 * 24
|
||||
}
|
||||
}
|
||||
|
||||
create_non_existing_group_and_subgroup( $input, $group, $subgroup );
|
||||
# check $cache_expiry isn't too large, Memcached::set requires it to be less than 30 days or it will be treated as if it were an absolute time stamp
|
||||
if ( $cache_expiry >= 2592000 ) {
|
||||
push @errors, { cache_expiry => $cache_expiry };
|
||||
}
|
||||
|
||||
my ( $is_sql_valid, $validation_errors ) = Koha::Report->new( { savedsql => $sql } )->is_sql_valid;
|
||||
push( @errors, @$validation_errors ) unless $is_sql_valid;
|
||||
create_non_existing_group_and_subgroup( $input, $group, $subgroup );
|
||||
|
||||
if (@errors) {
|
||||
$template->param(
|
||||
'errors' => \@errors,
|
||||
'sql' => $sql,
|
||||
);
|
||||
} else {
|
||||
my ( $is_sql_valid, $validation_errors ) = Koha::Report->new( { savedsql => $sql } )->is_sql_valid;
|
||||
push( @errors, @$validation_errors ) unless $is_sql_valid;
|
||||
|
||||
# Check defined SQL parameters for authorised value validity
|
||||
my $problematic_authvals = ValidateSQLParameters($sql);
|
||||
|
||||
if ( scalar @$problematic_authvals > 0 && not $save_anyway ) {
|
||||
|
||||
# There's at least one problematic parameter, report to the
|
||||
# GUI and provide all user input for further actions
|
||||
if (@errors) {
|
||||
$template->param(
|
||||
'id' => $id,
|
||||
'sql' => $sql,
|
||||
'reportname' => $reportname,
|
||||
'group' => $group,
|
||||
'subgroup' => $subgroup,
|
||||
'notes' => $notes,
|
||||
'public' => $public,
|
||||
'problematic_authvals' => $problematic_authvals,
|
||||
'warn_authval_problem' => 1,
|
||||
'phase_update' => 1,
|
||||
'errors' => \@errors,
|
||||
'sql' => $sql,
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
# No params problem found or asked to save anyway
|
||||
update_sql(
|
||||
$id,
|
||||
{
|
||||
sql => $sql,
|
||||
name => $reportname,
|
||||
group => $group,
|
||||
subgroup => $subgroup,
|
||||
notes => $notes,
|
||||
public => $public,
|
||||
cache_expiry => $cache_expiry,
|
||||
# Check defined SQL parameters for authorised value validity
|
||||
my $problematic_authvals = ValidateSQLParameters($sql);
|
||||
|
||||
if ( scalar @$problematic_authvals > 0 && not $save_anyway ) {
|
||||
|
||||
# There's at least one problematic parameter, report to the
|
||||
# GUI and provide all user input for further actions
|
||||
$template->param(
|
||||
'id' => $id,
|
||||
'sql' => $sql,
|
||||
'reportname' => $reportname,
|
||||
'group' => $group,
|
||||
'subgroup' => $subgroup,
|
||||
'notes' => $notes,
|
||||
'public' => $public,
|
||||
'problematic_authvals' => $problematic_authvals,
|
||||
'warn_authval_problem' => 1,
|
||||
'phase_update' => 1,
|
||||
'branches' => @branches,
|
||||
'report' => $report,
|
||||
);
|
||||
|
||||
} else {
|
||||
|
||||
# No params problem found or asked to save anyway
|
||||
update_sql(
|
||||
$id,
|
||||
{
|
||||
sql => $sql,
|
||||
name => $reportname,
|
||||
group => $group,
|
||||
subgroup => $subgroup,
|
||||
notes => $notes,
|
||||
public => $public,
|
||||
cache_expiry => $cache_expiry,
|
||||
}
|
||||
);
|
||||
|
||||
$report->store;
|
||||
$report->replace_library_limits( \@branches );
|
||||
|
||||
my $editsql = 1;
|
||||
if ( $op eq 'cud-update_and_run_sql' ) {
|
||||
$editsql = 0;
|
||||
}
|
||||
);
|
||||
|
||||
my $editsql = 1;
|
||||
if ( $op eq 'cud-update_and_run_sql' ) {
|
||||
$editsql = 0;
|
||||
$template->param(
|
||||
'save_successful' => 1,
|
||||
'reportname' => $reportname,
|
||||
'id' => $id,
|
||||
'report_creator' => $report->borrowernumber,
|
||||
'editsql' => $editsql,
|
||||
'sql' => $sql,
|
||||
'groups_with_subgroups' => groups_with_subgroups( $group, $subgroup ),
|
||||
'notes' => $notes,
|
||||
'cache_expiry' => $cache_expiry,
|
||||
'public' => $public,
|
||||
'usecache' => $usecache,
|
||||
'tables' => $tables,
|
||||
'report' => $report
|
||||
);
|
||||
logaction( "REPORTS", "MODIFY", $id, "$reportname | $sql" ) if C4::Context->preference("ReportsLog");
|
||||
}
|
||||
if ($usecache) {
|
||||
$template->param(
|
||||
cache_expiry => $cache_expiry,
|
||||
cache_expiry_units => $cache_expiry_units,
|
||||
);
|
||||
}
|
||||
if ( $op eq 'cud-update_and_run_sql' ) {
|
||||
$op = 'run';
|
||||
}
|
||||
|
||||
my $report = Koha::Reports->find($id);
|
||||
$template->param(
|
||||
'save_successful' => 1,
|
||||
'reportname' => $reportname,
|
||||
'id' => $id,
|
||||
'report_creator' => $report->borrowernumber,
|
||||
'editsql' => $editsql,
|
||||
'sql' => $sql,
|
||||
'groups_with_subgroups' => groups_with_subgroups( $group, $subgroup ),
|
||||
'notes' => $notes,
|
||||
'cache_expiry' => $cache_expiry,
|
||||
'public' => $public,
|
||||
'usecache' => $usecache,
|
||||
'tables' => $tables
|
||||
);
|
||||
logaction( "REPORTS", "MODIFY", $id, "$reportname | $sql" ) if C4::Context->preference("ReportsLog");
|
||||
}
|
||||
if ($usecache) {
|
||||
$template->param(
|
||||
cache_expiry => $cache_expiry,
|
||||
cache_expiry_units => $cache_expiry_units,
|
||||
);
|
||||
}
|
||||
if ( $op eq 'cud-update_and_run_sql' ) {
|
||||
$op = 'run';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -548,6 +588,11 @@ if ( !$op ) {
|
|||
my $public = $input->param('public');
|
||||
my $save_anyway = $input->param('save_anyway');
|
||||
my $tables = get_tables();
|
||||
my @branches = grep { $_ ne q{} } $input->multi_param('branches');
|
||||
|
||||
if ( $limit_reports_by_library && !$can_manage_report_limits ) {
|
||||
@branches = ( $logged_in_user->branchcode );
|
||||
}
|
||||
|
||||
# if we have the units, then we came from creating a report from SQL and thus need to handle converting units
|
||||
if ($cache_expiry_units) {
|
||||
|
|
@ -625,6 +670,9 @@ if ( !$op ) {
|
|||
public => $public,
|
||||
}
|
||||
);
|
||||
my $report = Koha::Reports->find($id);
|
||||
$report->replace_library_limits( \@branches );
|
||||
|
||||
logaction( "REPORTS", "ADD", $id, "$name | $sql" ) if C4::Context->preference("ReportsLog");
|
||||
my $new_report = Koha::Reports->find($id);
|
||||
$template->param(
|
||||
|
|
@ -639,7 +687,8 @@ if ( !$op ) {
|
|||
'cache_expiry' => $cache_expiry,
|
||||
'public' => $public,
|
||||
'usecache' => $usecache,
|
||||
'tables' => $tables
|
||||
'tables' => $tables,
|
||||
'report' => $report,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -657,148 +706,162 @@ if ( !$op ) {
|
|||
} elsif ( $op eq 'export' ) {
|
||||
|
||||
# export results to tab separated text or CSV
|
||||
my $report_id = $input->param('id');
|
||||
my $report = Koha::Reports->find($report_id);
|
||||
my $sql = $report->savedsql;
|
||||
my @param_names = $input->multi_param('param_name');
|
||||
my @sql_params = $input->multi_param('sql_params');
|
||||
my $format = $input->param('format');
|
||||
my $reportname = $input->param('reportname');
|
||||
my $reportfilename =
|
||||
$reportname ? "$report_id-$reportname-reportresults.$format" : "$report_id-reportresults.$format";
|
||||
my $scrubber = C4::Scrubber->new();
|
||||
if ($report) {
|
||||
my $sql = $report->savedsql;
|
||||
my @param_names = $input->multi_param('param_name');
|
||||
my @sql_params = $input->multi_param('sql_params');
|
||||
my $format = $input->param('format');
|
||||
my $reportname = $input->param('reportname');
|
||||
my $reportfilename =
|
||||
$reportname ? "$report_id-$reportname-reportresults.$format" : "$report_id-reportresults.$format";
|
||||
my $scrubber = C4::Scrubber->new();
|
||||
|
||||
eval { ( $sql, undef ) = $report->prep_report( \@param_names, \@sql_params, { export => 1 } ); };
|
||||
if ( my $e = $@ ) {
|
||||
if ( ref($e) eq 'Koha::Exceptions::Report::DuplicateRunning' ) {
|
||||
print $input->header(
|
||||
-status => '429 Too Many Requests',
|
||||
-type => 'text/plain; charset=UTF-8',
|
||||
);
|
||||
print "This report is already running. Please wait for it to finish before running it again.\n";
|
||||
exit;
|
||||
}
|
||||
die $e;
|
||||
}
|
||||
my ( $sth, $q_errors ) = execute_query( { sql => $sql, report_id => $report_id } );
|
||||
unless ( $q_errors and @$q_errors ) {
|
||||
my ( $type, $content );
|
||||
if ( $format eq 'tab' ) {
|
||||
$type = 'application/octet-stream';
|
||||
$content .= join( "\t", header_cell_values($sth) ) . "\n";
|
||||
$content = $scrubber->scrub( Encode::decode( 'UTF-8', $content ) );
|
||||
while ( my $row = $sth->fetchrow_arrayref() ) {
|
||||
$content .= $scrubber->scrub( join( "\t", map { $_ // '' } @$row ) ) . "\n";
|
||||
eval { ( $sql, undef ) = $report->prep_report( \@param_names, \@sql_params, { export => 1 } ); };
|
||||
if ( my $e = $@ ) {
|
||||
if ( ref($e) eq 'Koha::Exceptions::Report::DuplicateRunning' ) {
|
||||
print $input->header(
|
||||
-status => '429 Too Many Requests',
|
||||
-type => 'text/plain; charset=UTF-8',
|
||||
);
|
||||
print "This report is already running. Please wait for it to finish before running it again.\n";
|
||||
exit;
|
||||
}
|
||||
} else {
|
||||
if ( $format eq 'csv' ) {
|
||||
my $delimiter = C4::Context->csv_delimiter;
|
||||
$type = 'application/csv';
|
||||
die $e;
|
||||
}
|
||||
my ( $sth, $q_errors ) = execute_query( { sql => $sql, report_id => $report_id } );
|
||||
|
||||
# Add BOM for UTF-8 encoded CSV
|
||||
$content .= "\xEF\xBB\xBF";
|
||||
|
||||
my $csv =
|
||||
Text::CSV::Encoded->new( { encoding_out => 'UTF-8', sep_char => $delimiter, formula => 'empty' } );
|
||||
$csv or die "Text::CSV::Encoded->new({binary => 1}) FAILED: " . Text::CSV::Encoded->error_diag();
|
||||
if ( $csv->combine( header_cell_values($sth) ) ) {
|
||||
$content .= $scrubber->scrub( Encode::decode( 'UTF-8', $csv->string() ) ) . "\n";
|
||||
|
||||
} else {
|
||||
push @$q_errors, { combine => 'HEADER ROW: ' . $csv->error_diag() };
|
||||
}
|
||||
unless ( $q_errors and @$q_errors ) {
|
||||
my ( $type, $content );
|
||||
if ( $format eq 'tab' ) {
|
||||
$type = 'application/octet-stream';
|
||||
$content .= join( "\t", header_cell_values($sth) ) . "\n";
|
||||
$content = $scrubber->scrub( Encode::decode( 'UTF-8', $content ) );
|
||||
while ( my $row = $sth->fetchrow_arrayref() ) {
|
||||
if ( $csv->combine(@$row) ) {
|
||||
$content .= $scrubber->scrub( $csv->string() ) . "\n";
|
||||
$content .= $scrubber->scrub( join( "\t", map { $_ // '' } @$row ) ) . "\n";
|
||||
}
|
||||
} else {
|
||||
if ( $format eq 'csv' ) {
|
||||
my $delimiter = C4::Context->csv_delimiter;
|
||||
$type = 'application/csv';
|
||||
|
||||
# Add BOM for UTF-8 encoded CSV
|
||||
$content .= "\xEF\xBB\xBF";
|
||||
|
||||
my $csv =
|
||||
Text::CSV::Encoded->new(
|
||||
{ encoding_out => 'UTF-8', sep_char => $delimiter, formula => 'empty' } );
|
||||
$csv or die "Text::CSV::Encoded->new({binary => 1}) FAILED: " . Text::CSV::Encoded->error_diag();
|
||||
if ( $csv->combine( header_cell_values($sth) ) ) {
|
||||
$content .= $scrubber->scrub( Encode::decode( 'UTF-8', $csv->string() ) ) . "\n";
|
||||
|
||||
} else {
|
||||
push @$q_errors, { combine => $csv->error_diag() };
|
||||
push @$q_errors, { combine => 'HEADER ROW: ' . $csv->error_diag() };
|
||||
}
|
||||
}
|
||||
} elsif ( $format eq 'ods' && C4::Context->preference('ReportsExportFormatODS') ) {
|
||||
$type = 'application/vnd.oasis.opendocument.spreadsheet';
|
||||
my $ods_fh = File::Temp->new( UNLINK => 0 );
|
||||
my $ods_filepath = $ods_fh->filename;
|
||||
my $ods_content;
|
||||
|
||||
# First line is headers
|
||||
my @headers = header_cell_values($sth);
|
||||
push @$ods_content, \@headers;
|
||||
|
||||
# Other line in Unicode
|
||||
my $sql_rows = $sth->fetchall_arrayref();
|
||||
foreach my $sql_row (@$sql_rows) {
|
||||
my @content_row;
|
||||
foreach my $sql_cell (@$sql_row) {
|
||||
push @content_row, $scrubber->scrub( Encode::encode( 'UTF8', $sql_cell ) );
|
||||
while ( my $row = $sth->fetchrow_arrayref() ) {
|
||||
if ( $csv->combine(@$row) ) {
|
||||
$content .= $scrubber->scrub( $csv->string() ) . "\n";
|
||||
|
||||
} else {
|
||||
push @$q_errors, { combine => $csv->error_diag() };
|
||||
}
|
||||
}
|
||||
push @$ods_content, \@content_row;
|
||||
} elsif ( $format eq 'ods' && C4::Context->preference('ReportsExportFormatODS') ) {
|
||||
$type = 'application/vnd.oasis.opendocument.spreadsheet';
|
||||
my $ods_fh = File::Temp->new( UNLINK => 0 );
|
||||
my $ods_filepath = $ods_fh->filename;
|
||||
my $ods_content;
|
||||
|
||||
# First line is headers
|
||||
my @headers = header_cell_values($sth);
|
||||
push @$ods_content, \@headers;
|
||||
|
||||
# Other line in Unicode
|
||||
my $sql_rows = $sth->fetchall_arrayref();
|
||||
foreach my $sql_row (@$sql_rows) {
|
||||
my @content_row;
|
||||
foreach my $sql_cell (@$sql_row) {
|
||||
push @content_row, $scrubber->scrub( Encode::encode( 'UTF8', $sql_cell ) );
|
||||
|
||||
}
|
||||
push @$ods_content, \@content_row;
|
||||
}
|
||||
|
||||
# Process
|
||||
generate_ods( $ods_filepath, $ods_content );
|
||||
|
||||
# Output
|
||||
binmode(STDOUT);
|
||||
open $ods_fh, '<', $ods_filepath;
|
||||
print $input->header(
|
||||
-type => $type,
|
||||
-attachment => $reportfilename
|
||||
);
|
||||
print $_ while <$ods_fh>;
|
||||
unlink $ods_filepath;
|
||||
} elsif ( $format eq 'template' ) {
|
||||
my $template_id = $input->param('template');
|
||||
my $notice_template = Koha::Notice::Templates->find($template_id);
|
||||
my $data = $sth->fetchall_arrayref( {} );
|
||||
$content = process_tt(
|
||||
$notice_template->content,
|
||||
{
|
||||
data => $data,
|
||||
report_id => $report_id,
|
||||
for_download => 1,
|
||||
}
|
||||
);
|
||||
$reportfilename = process_tt(
|
||||
$notice_template->title,
|
||||
{
|
||||
data => $data,
|
||||
report_id => $report_id,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
# Process
|
||||
generate_ods( $ods_filepath, $ods_content );
|
||||
|
||||
# Output
|
||||
binmode(STDOUT);
|
||||
open $ods_fh, '<', $ods_filepath;
|
||||
unless ( $format eq 'ods' ) {
|
||||
print $input->header(
|
||||
-type => $type,
|
||||
-attachment => $reportfilename
|
||||
);
|
||||
print $_ while <$ods_fh>;
|
||||
unlink $ods_filepath;
|
||||
} elsif ( $format eq 'template' ) {
|
||||
my $template_code = $input->param('template_code');
|
||||
my $data = $sth->fetchall_arrayref( {} );
|
||||
|
||||
my $rendered = GetPreparedLetter(
|
||||
module => 'report',
|
||||
letter_code => $template_code,
|
||||
objects => { data => $data, report_id => $report_id, for_download => 1 }
|
||||
);
|
||||
$content = $rendered->{content};
|
||||
$reportfilename = $rendered->{title};
|
||||
print $content;
|
||||
}
|
||||
}
|
||||
|
||||
unless ( $format eq 'ods' ) {
|
||||
print $input->header(
|
||||
-type => $type,
|
||||
-attachment => $reportfilename
|
||||
);
|
||||
print $content;
|
||||
foreach my $err ( @$q_errors, @errors ) {
|
||||
print "# ERROR: " . ( map { $_ . ": " . $err->{$_} } keys %$err ) . "\n";
|
||||
} # here we print all the non-fatal errors at the end. Not super smooth, but better than nothing.
|
||||
exit;
|
||||
}
|
||||
|
||||
foreach my $err ( @$q_errors, @errors ) {
|
||||
print "# ERROR: " . ( map { $_ . ": " . $err->{$_} } keys %$err ) . "\n";
|
||||
} # here we print all the non-fatal errors at the end. Not super smooth, but better than nothing.
|
||||
exit;
|
||||
$template->param(
|
||||
'sql' => $sql,
|
||||
'execute' => 1,
|
||||
'name' => 'Error exporting report!',
|
||||
'notes' => '',
|
||||
'errors' => $q_errors,
|
||||
);
|
||||
}
|
||||
$template->param(
|
||||
'sql' => $sql,
|
||||
'execute' => 1,
|
||||
'name' => 'Error exporting report!',
|
||||
'notes' => '',
|
||||
'errors' => $q_errors,
|
||||
);
|
||||
|
||||
} elsif ( $op eq 'add_form_sql' || $op eq 'duplicate' ) {
|
||||
|
||||
my ( $group, $subgroup, $sql, $reportname, $notes );
|
||||
my ( $group, $subgroup, $sql, $reportname, $notes, @branches, $report );
|
||||
if ( $input->param('sql') ) {
|
||||
$group = $input->param('report_group');
|
||||
$subgroup = $input->param('report_subgroup');
|
||||
$sql = $input->param('sql') // '';
|
||||
$reportname = $input->param('reportname') // '';
|
||||
$notes = $input->param('notes') // '';
|
||||
@branches = grep { $_ ne q{} } $input->multi_param('branches');
|
||||
|
||||
} elsif ( my $report_id = $input->param('id') ) {
|
||||
my $report = Koha::Reports->find($report_id);
|
||||
$report = Koha::Reports->find($report_id);
|
||||
$group = $report->report_group;
|
||||
$subgroup = $report->report_subgroup;
|
||||
$sql = $report->savedsql // '';
|
||||
$reportname = $report->report_name // '';
|
||||
$notes = $report->notes // '';
|
||||
@branches = grep { $_ ne q{} } $input->multi_param('branches');
|
||||
|
||||
}
|
||||
|
||||
my $tables = get_tables();
|
||||
|
|
@ -813,6 +876,7 @@ if ( !$op ) {
|
|||
'cache_expiry' => 300,
|
||||
'usecache' => $usecache,
|
||||
'tables' => $tables,
|
||||
'report' => $report,
|
||||
|
||||
);
|
||||
}
|
||||
|
|
@ -822,7 +886,6 @@ if ( $op eq 'run' ) {
|
|||
# execute a saved report
|
||||
my $limit = $input->param('limit') || 20;
|
||||
my $offset = 0;
|
||||
my $report_id = $input->param('id');
|
||||
my @sql_params = $input->multi_param('sql_params');
|
||||
my @param_names = $input->multi_param('param_name');
|
||||
my $template_code = $input->param('template_code');
|
||||
|
|
@ -840,7 +903,7 @@ if ( $op eq 'run' ) {
|
|||
);
|
||||
|
||||
my ( $sql, $original_sql, $type, $name, $notes );
|
||||
if ( my $report = Koha::Reports->find($report_id) ) {
|
||||
if ($report) {
|
||||
$sql = $original_sql = $report->savedsql;
|
||||
$name = $report->report_name;
|
||||
$notes = $report->notes;
|
||||
|
|
@ -1115,8 +1178,6 @@ if ( $op eq 'run' ) {
|
|||
'param_names' => \@param_names,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
push @errors, { no_sql_for_id => $report_id };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1145,11 +1206,25 @@ if ( $op eq 'list' || $op eq 'convert' ) {
|
|||
|
||||
# use a saved report
|
||||
# get list of reports and display them
|
||||
my $group = $input->param('group');
|
||||
my $subgroup = $input->param('subgroup');
|
||||
my $group = $input->param('group');
|
||||
my $subgroup = $input->param('subgroup');
|
||||
my $show_all_filter = $filter->{show_all_reports};
|
||||
$filter->{group} = $group;
|
||||
$filter->{subgroup} = $subgroup;
|
||||
|
||||
if ($show_all_filter) {
|
||||
$filter->{show_all_reports} = $can_manage_report_limits ? $show_all_filter : 0;
|
||||
} else {
|
||||
$filter->{show_all_reports} = 0;
|
||||
}
|
||||
|
||||
my $reports = get_saved_reports($filter);
|
||||
|
||||
my $library_limit_active =
|
||||
$limit_reports_by_library
|
||||
&& $can_manage_report_limits
|
||||
&& !$filter->{show_all_reports};
|
||||
|
||||
my $has_obsolete_reports;
|
||||
for my $report (@$reports) {
|
||||
$report->{results} = C4::Reports::Guided::get_results( $report->{id} );
|
||||
|
|
@ -1159,13 +1234,15 @@ if ( $op eq 'list' || $op eq 'convert' ) {
|
|||
}
|
||||
}
|
||||
$template->param(
|
||||
'manamsg' => $input->param('manamsg') || '',
|
||||
'saved1' => 1,
|
||||
'savedreports' => $reports,
|
||||
'usecache' => $usecache,
|
||||
'groups_with_subgroups' => groups_with_subgroups( $group, $subgroup ),
|
||||
filters => $filter,
|
||||
has_obsolete_reports => $has_obsolete_reports,
|
||||
'manamsg' => $input->param('manamsg') || '',
|
||||
'saved1' => 1,
|
||||
'savedreports' => $reports,
|
||||
'usecache' => $usecache,
|
||||
'groups_with_subgroups' => groups_with_subgroups( $group, $subgroup ),
|
||||
filters => $filter,
|
||||
has_obsolete_reports => $has_obsolete_reports,
|
||||
library_limit_active => $library_limit_active,
|
||||
can_manage_report_limits => $can_manage_report_limits,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -1276,4 +1353,3 @@ sub create_non_existing_group_and_subgroup {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ subtest 'normal staff user test' => sub {
|
|||
'CAN_user_reports_delete_own_reports' => 1,
|
||||
'CAN_user_reports_delete_all_reports' => 1,
|
||||
'CAN_user_reports_execute_reports' => 1,
|
||||
'CAN_user_reports_manage_report_limits' => 1,
|
||||
};
|
||||
is_deeply( $authz, $expected, 'Expected permissions generated for normal staff user' );
|
||||
};
|
||||
|
|
@ -237,6 +238,7 @@ subtest 'superlibrarian tests' => sub {
|
|||
'CAN_user_reports_delete_own_reports' => 1,
|
||||
'CAN_user_reports_delete_all_reports' => 1,
|
||||
'CAN_user_reports_execute_reports' => 1,
|
||||
'CAN_user_reports_manage_report_limits' => 1,
|
||||
'CAN_user_reports' => 1,
|
||||
'CAN_user_reserveforothers_modify_holds_priority' => 1,
|
||||
'CAN_user_reserveforothers_place_holds' => 1,
|
||||
|
|
|
|||
|
|
@ -287,6 +287,160 @@ subtest 'apply_execution_time_limit' => sub {
|
|||
Koha::Report->apply_execution_time_limit($sql),
|
||||
qr!^(?i:select) /\*\+ MAX_EXECUTION_TIME\(1500\) \*/ \* FROM biblio$!,
|
||||
'MySQL max execution time hint applied'
|
||||
subtest 'reports_branches are added and removed from report_branches table' => sub {
|
||||
plan tests => 4;
|
||||
|
||||
my $updated_nb_of_reports = Koha::Reports->search->count;
|
||||
my $report = Koha::Report->new(
|
||||
{
|
||||
report_name => 'report_name_for_test_1',
|
||||
savedsql => 'SELECT * FROM items WHERE itemnumber IN <<Test|list>>',
|
||||
}
|
||||
)->store;
|
||||
|
||||
my $id = $report->id;
|
||||
my $library1 = $builder->build_object( { class => 'Koha::Libraries' } );
|
||||
my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
|
||||
my $library3 = $builder->build_object( { class => 'Koha::Libraries' } );
|
||||
my @branches = ( $library1->branchcode, $library2->branchcode, $library3->branchcode );
|
||||
|
||||
$report->replace_library_limits( \@branches );
|
||||
|
||||
my @branches_loop = $report->get_library_limits->as_list;
|
||||
is( scalar @branches_loop, 3, '3 branches added to report_branches table' );
|
||||
|
||||
$report->replace_library_limits( [ $library1->branchcode, $library2->branchcode ] );
|
||||
|
||||
@branches_loop = $report->get_library_limits->as_list;
|
||||
is( scalar @branches_loop, 2, '1 branch removed from report_branches table' );
|
||||
|
||||
$report->delete;
|
||||
is( Koha::Reports->search->count, $updated_nb_of_reports, 'Report deleted, count is back to original' );
|
||||
is(
|
||||
$schema->resultset('ReportsBranch')->search( { report_id => $id } )->count,
|
||||
0,
|
||||
'No branches left in reports_branches table after report deletion'
|
||||
);
|
||||
};
|
||||
|
||||
subtest 'can_manage_limits and can_access' => sub {
|
||||
plan tests => 21;
|
||||
|
||||
my $libraryA = $builder->build_object( { class => 'Koha::Libraries' } );
|
||||
my $libraryB = $builder->build_object( { class => 'Koha::Libraries' } );
|
||||
my $branchA = $libraryA->branchcode;
|
||||
my $branchB = $libraryB->branchcode;
|
||||
|
||||
my $super_patron =
|
||||
$builder->build_object( { class => 'Koha::Patrons', value => { flags => 1, branchcode => $branchA } } );
|
||||
my $mgr_patron = $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $branchA } } );
|
||||
my $basic_patron = $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $branchA } } );
|
||||
|
||||
# Grant reports => manage_report_limits to manager patron (create permission if missing)
|
||||
my $perm = $schema->resultset('Permission')->find( { module_bit => 16, code => 'manage_report_limits' } )
|
||||
// $schema->resultset('Permission')
|
||||
->create( { module_bit => 16, code => 'manage_report_limits', description => 'Manage report limits' } );
|
||||
$schema->resultset('UserPermission')
|
||||
->create( { borrowernumber => $mgr_patron->borrowernumber, module_bit => 16, code => 'manage_report_limits' } );
|
||||
|
||||
# Helper to create reports
|
||||
my $r_no = Koha::Report->new( { report_name => 'No limits', savedsql => 'SELECT 1' } )->store;
|
||||
my $r_A = Koha::Report->new( { report_name => 'Limit A', savedsql => 'SELECT 1' } )->store;
|
||||
my $r_B = Koha::Report->new( { report_name => 'Limit B', savedsql => 'SELECT 1' } )->store;
|
||||
my $r_AB = Koha::Report->new( { report_name => 'Limit AB', savedsql => 'SELECT 1' } )->store;
|
||||
|
||||
$r_A->replace_library_limits( [$branchA] );
|
||||
$r_B->replace_library_limits( [$branchB] );
|
||||
$r_AB->replace_library_limits( [ $branchA, $branchB ] );
|
||||
|
||||
# Preference ON, branch A
|
||||
t::lib::Mocks::mock_preference( 'LimitReportsByLibrary', 1 );
|
||||
t::lib::Mocks::mock_userenv( { branchcode => $branchA } );
|
||||
|
||||
ok( Koha::Report->can_manage_limits($super_patron), 'pref ON: super manages limits' );
|
||||
ok( Koha::Report->can_manage_limits($mgr_patron), 'pref ON: manager manages limits' );
|
||||
ok( !Koha::Report->can_manage_limits($basic_patron), 'pref ON: basic cannot manage limits' );
|
||||
|
||||
# Basic patron (branch A)
|
||||
ok( $r_no->can_access($basic_patron), 'pref ON: no limits accessible' );
|
||||
ok( $r_A->can_access($basic_patron), 'pref ON: limited includes branch accessible' );
|
||||
ok( !$r_B->can_access($basic_patron), 'pref ON: limited excludes branch denied' );
|
||||
ok( $r_AB->can_access($basic_patron), 'pref ON: multi includes branch accessible' );
|
||||
|
||||
# Manager bypass (branch A)
|
||||
ok( $r_A->can_access($mgr_patron), 'pref ON: manager sees limited A' );
|
||||
ok( $r_B->can_access($mgr_patron), 'pref ON: manager sees limited B' );
|
||||
ok( $r_AB->can_access($mgr_patron), 'pref ON: manager sees multi AB' );
|
||||
ok( $r_no->can_access($mgr_patron), 'pref ON: manager sees no limits' );
|
||||
|
||||
# Superlibrarian bypass (branch A)
|
||||
ok( $r_A->can_access($super_patron), 'pref ON: super sees limited A' );
|
||||
ok( $r_B->can_access($super_patron), 'pref ON: super sees limited B' );
|
||||
ok( $r_AB->can_access($super_patron), 'pref ON: super sees multi AB' );
|
||||
|
||||
# Preference OFF (everything accessible, manage disabled)
|
||||
t::lib::Mocks::mock_preference( 'LimitReportsByLibrary', 0 );
|
||||
ok( !Koha::Report->can_manage_limits($super_patron), 'pref OFF: super cannot manage limits' );
|
||||
ok( !Koha::Report->can_manage_limits($mgr_patron), 'pref OFF: manager cannot manage limits' );
|
||||
ok( !Koha::Report->can_manage_limits($basic_patron), 'pref OFF: basic cannot manage limits' );
|
||||
ok( $r_B->can_access($basic_patron), 'pref OFF: limited excludes branch still accessible' );
|
||||
ok( $r_A->can_access($basic_patron), 'pref OFF: limited includes branch accessible' );
|
||||
ok( $r_AB->can_access($basic_patron), 'pref OFF: multi-limit accessible' );
|
||||
ok( $r_no->can_access($basic_patron), 'pref OFF: no limits accessible' );
|
||||
};
|
||||
|
||||
subtest 'library limits enforcement on save and update' => sub {
|
||||
plan tests => 5;
|
||||
|
||||
my $libraryA = $builder->build_object( { class => 'Koha::Libraries' } );
|
||||
my $branchA = $libraryA->branchcode;
|
||||
|
||||
my $basic_patron = $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $branchA } } );
|
||||
|
||||
# Ensure manage_report_limits permission exists and is NOT granted to basic_patron
|
||||
my $perm = $schema->resultset('Permission')->find( { module_bit => 16, code => 'manage_report_limits' } )
|
||||
// $schema->resultset('Permission')
|
||||
->create( { module_bit => 16, code => 'manage_report_limits', description => 'Manage report limits' } );
|
||||
|
||||
# Create report path
|
||||
|
||||
# pref ON, no permission: creator branch auto-assigned
|
||||
t::lib::Mocks::mock_preference( 'LimitReportsByLibrary', 1 );
|
||||
my $report_create = Koha::Report->new( { report_name => 'limit test create', savedsql => 'SELECT 1' } )->store;
|
||||
my $can_manage = Koha::Report->can_manage_limits($basic_patron);
|
||||
my @branches = $can_manage ? () : ( $basic_patron->branchcode );
|
||||
$report_create->replace_library_limits( \@branches );
|
||||
my @limits = $report_create->get_library_limits->as_list;
|
||||
is( scalar @limits, 1, 'pref ON, no perm: create assigns exactly one branch limit' );
|
||||
is( $limits[0]->branchcode, $branchA, 'pref ON, no perm: create limit is the creator branchcode' );
|
||||
|
||||
# pref OFF: no branches from form, no limits stored
|
||||
t::lib::Mocks::mock_preference( 'LimitReportsByLibrary', 0 );
|
||||
my $report_off = Koha::Report->new( { report_name => 'limit test pref off', savedsql => 'SELECT 1' } )->store;
|
||||
$report_off->replace_library_limits( [] );
|
||||
is( $report_off->get_library_limits, undef, 'pref OFF: no limits stored on create' );
|
||||
|
||||
# Update report path
|
||||
t::lib::Mocks::mock_preference( 'LimitReportsByLibrary', 1 );
|
||||
|
||||
# pref ON, no permission, report has existing limits: limits preserved unchanged
|
||||
my $report_update = Koha::Report->new( { report_name => 'limit test update', savedsql => 'SELECT 1' } )->store;
|
||||
$report_update->replace_library_limits( [$branchA] );
|
||||
my $existing_limits = $report_update->get_library_limits;
|
||||
my @preserved = $existing_limits ? map { $_->branchcode } $existing_limits->as_list : ();
|
||||
$report_update->replace_library_limits( \@preserved );
|
||||
my @updated_limits = $report_update->get_library_limits->as_list;
|
||||
is( scalar @updated_limits, 1, 'pref ON, no perm: update preserves existing limits unchanged' );
|
||||
|
||||
# pref ON, no permission, report has no existing limits: stays limit-free
|
||||
my $report_no_limits =
|
||||
Koha::Report->new( { report_name => 'limit test no limits', savedsql => 'SELECT 1' } )->store;
|
||||
my $existing_limits2 = $report_no_limits->get_library_limits;
|
||||
my @branches2 = $existing_limits2 ? map { $_->branchcode } $existing_limits2->as_list : ();
|
||||
$report_no_limits->replace_library_limits( \@branches2 );
|
||||
is(
|
||||
$report_no_limits->get_library_limits, undef,
|
||||
'pref ON, no perm: update with no existing limits stays limit-free'
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ use Modern::Perl;
|
|||
|
||||
use Test::NoWarnings;
|
||||
use Test::MockModule qw(strict);
|
||||
use Test::More tests => 14;
|
||||
use Test::More tests => 15;
|
||||
use Test::Warn;
|
||||
|
||||
use t::lib::TestBuilder;
|
||||
|
|
@ -333,6 +333,91 @@ subtest 'get_saved_reports' => sub {
|
|||
);
|
||||
};
|
||||
|
||||
subtest 'get_saved_reports branch limits' => sub {
|
||||
my $schema = Koha::Database->new->schema;
|
||||
my $dbh = C4::Context->dbh;
|
||||
|
||||
# Skip gracefully if the feature table is not present (e.g. older schema)
|
||||
my $sth = $dbh->prepare(q{SHOW TABLES LIKE 'reports_library_limits'});
|
||||
$sth->execute;
|
||||
my ($exists) = $sth->fetchrow_array;
|
||||
unless ($exists) {
|
||||
plan skip_all => 'reports_library_limits table not present; skipping branch limits tests';
|
||||
return;
|
||||
}
|
||||
|
||||
plan tests => 13;
|
||||
|
||||
# Clean related tables
|
||||
$dbh->do(q|DELETE FROM saved_sql|);
|
||||
$dbh->do(q|DELETE FROM saved_reports|);
|
||||
$dbh->do(q|DELETE FROM reports_library_limits|);
|
||||
|
||||
# Build two libraries (branches)
|
||||
my $lib_a = $builder->build( { source => 'Branch', value => 'Library A' } );
|
||||
my $lib_b = $builder->build( { source => 'Branch', value => 'Library B' } );
|
||||
|
||||
# Mock userenv to lib_a
|
||||
t::lib::Mocks::mock_userenv( { branchcode => $lib_a->{branchcode} } );
|
||||
|
||||
# Build three reports
|
||||
my @ids;
|
||||
foreach my $n ( 1 .. 3 ) {
|
||||
push @ids, save_report(
|
||||
{
|
||||
borrowernumber => $builder->build( { source => 'Borrower' } )->{borrowernumber},
|
||||
sql => "SELECT $n",
|
||||
name => "Report $n",
|
||||
area => 'CIRC',
|
||||
group => 'G',
|
||||
subgroup => 'SG',
|
||||
type => 'T',
|
||||
notes => undef,
|
||||
cache_expiry => undef,
|
||||
public => 0,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
my ( $r1, $r2, $r3 ) = map { Koha::Reports->find($_) } @ids;
|
||||
|
||||
# Apply library limits to first two reports (r1 => lib_a only, r2 => lib_b only)
|
||||
$r1->replace_library_limits( [ $lib_a->{branchcode} ] );
|
||||
$r2->replace_library_limits( [ $lib_b->{branchcode} ] );
|
||||
|
||||
# r3 has no limits
|
||||
|
||||
# Preference OFF: should ignore limits, return all three, no library_limits metadata
|
||||
t::lib::Mocks::mock_preference( 'LimitReportsByLibrary', 0 );
|
||||
my $all_pref_off = get_saved_reports();
|
||||
is( scalar(@$all_pref_off), 3, 'Pref OFF returns all reports' );
|
||||
ok( !defined $all_pref_off->[0]{library_limits}, 'Pref OFF does not attach metadata' );
|
||||
|
||||
# Preference ON, filtering applied (userenv branch is lib_a)
|
||||
t::lib::Mocks::mock_preference( 'LimitReportsByLibrary', 1 );
|
||||
my $filtered = get_saved_reports();
|
||||
is( scalar(@$filtered), 2, 'Pref ON filters reports by user branch and unrestricted reports' );
|
||||
my @filtered_ids = sort map { $_->{id} } @$filtered;
|
||||
my @expected_ids = sort ( $r1->id, $r3->id ); # r1 limited to lib_a + r3 unrestricted
|
||||
is_deeply( \@filtered_ids, \@expected_ids, 'Filtered list contains branch-limited and unrestricted reports' );
|
||||
foreach my $r (@$filtered) {
|
||||
ok( defined $r->{library_limits}, 'Metadata attached on pref ON filtered path' );
|
||||
}
|
||||
my ($meta_r1) = grep { $_->{id} == $r1->id } @$filtered;
|
||||
is_deeply( $meta_r1->{library_limits}, [ $lib_a->{branchcode} ], 'r1 metadata lists its single limit' );
|
||||
my ($meta_r3) = grep { $_->{id} == $r3->id } @$filtered;
|
||||
is_deeply( $meta_r3->{library_limits}, [], 'Unrestricted r3 has empty limits array' );
|
||||
|
||||
# Preference ON + show_all_reports flag: should return all, with metadata for all
|
||||
my $show_all = get_saved_reports( { show_all_reports => 1 } );
|
||||
is( scalar(@$show_all), 3, 'show_all_reports returns all reports when pref ON' );
|
||||
foreach my $r (@$show_all) {
|
||||
ok( defined $r->{library_limits}, 'Metadata attached for each report in show_all path' );
|
||||
}
|
||||
my ($show_r2) = grep { $_->{id} == $r2->id } @$show_all;
|
||||
is_deeply( $show_r2->{library_limits}, [ $lib_b->{branchcode} ], 'r2 metadata lists its limit under show_all' );
|
||||
};
|
||||
|
||||
subtest 'Ensure last_run is populated' => sub {
|
||||
plan tests => 3;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ use C4::Output qw( output_html_with_http_headers );
|
|||
use Koha::DateUtils qw( dt_from_string );
|
||||
use Koha::Reports;
|
||||
use Koha::Email;
|
||||
use Koha::Patrons;
|
||||
use Koha::Report;
|
||||
|
||||
my $input = CGI->new;
|
||||
my $base;
|
||||
|
|
@ -48,8 +50,20 @@ my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
|
|||
}
|
||||
);
|
||||
|
||||
my $op = $input->param('op') // q{};
|
||||
my $id = $input->param('id');
|
||||
my $op = $input->param('op') // q{};
|
||||
my $id = $input->param('id');
|
||||
my $selected_report_id = $input->param('report');
|
||||
my $selected_report = $selected_report_id ? Koha::Reports->find($selected_report_id) : undef;
|
||||
if ( $op eq 'cud-add' && $selected_report_id ) {
|
||||
my $logged_in_user = Koha::Patrons->find($borrowernumber);
|
||||
if ( !$selected_report ) {
|
||||
$template->param( no_sql_for_id => $selected_report_id, access_denied => 1 );
|
||||
$op = '';
|
||||
} elsif ( !$selected_report->can_access($logged_in_user) ) {
|
||||
$template->param( access_denied => 1, id => $selected_report_id );
|
||||
$op = '';
|
||||
}
|
||||
}
|
||||
|
||||
if ( $op eq 'cud-add' ) {
|
||||
|
||||
|
|
@ -111,14 +125,31 @@ foreach my $job ( values %$jobs ) {
|
|||
|
||||
@jobloop = sort { $a->{TIME} cmp $b->{TIME} } @jobloop;
|
||||
|
||||
my $reports = get_saved_reports();
|
||||
my $logged_in_user = Koha::Patrons->find($borrowernumber);
|
||||
my $can_manage_limits = Koha::Report->can_manage_limits($logged_in_user);
|
||||
|
||||
my $reports =
|
||||
$can_manage_limits
|
||||
? get_saved_reports( { show_all_reports => 1 } )
|
||||
: get_saved_reports();
|
||||
|
||||
my @final_reports;
|
||||
if ($can_manage_limits) {
|
||||
@final_reports = @$reports;
|
||||
} else {
|
||||
@final_reports = grep {
|
||||
my $r_obj = Koha::Reports->find( $_->{id} );
|
||||
$r_obj && $r_obj->can_access($logged_in_user);
|
||||
} @$reports;
|
||||
}
|
||||
|
||||
if ( defined $id ) {
|
||||
foreach my $report (@$reports) {
|
||||
foreach my $report (@final_reports) {
|
||||
$report->{'selected'} = 1 if $report->{'id'} eq $id;
|
||||
}
|
||||
}
|
||||
|
||||
$template->param( 'savedreports' => $reports );
|
||||
$template->param( 'savedreports' => \@final_reports );
|
||||
$template->param( JOBS => \@jobloop );
|
||||
my $time = localtime(time);
|
||||
$template->param( 'time' => $time );
|
||||
|
|
|
|||
Loading…
Reference in New Issue