View | Details | Raw Unified | Return to bug 16631
Collapse All | Expand All

(-)a/C4/Reports/Guided.pm (-1 / +48 lines)
Lines 878-884 sub get_saved_reports { Link Here
878
878
879
    my $result = $dbh->selectall_arrayref( $query, { Slice => {} }, @args );
879
    my $result = $dbh->selectall_arrayref( $query, { Slice => {} }, @args );
880
880
881
    return $result;
881
    my $limit_pref_enabled = C4::Context->preference('LimitReportsByBranch');
882
883
    return $result unless $limit_pref_enabled;
884
885
    # Preference enabled AND showing all: attach library limits metadata, but do not filter
886
    if ( $filter->{show_all_reports} ) {
887
        foreach my $report (@$result) {
888
            my $report_obj = Koha::Reports->find( $report->{id} );
889
            if ($report_obj) {
890
                my $limits_rs = $report_obj->get_library_limits;
891
                if ($limits_rs) {
892
                    my @branches =
893
                        map { { branchcode => $_->branchcode, branchname => $_->branchname } } $limits_rs->as_list;
894
                    $report->{library_limits} = \@branches;
895
                } else {
896
                    $report->{library_limits} = [];
897
                }
898
            } else {
899
                $report->{library_limits} = [];
900
            }
901
        }
902
        return $result;
903
    }
904
905
    # Preference enabled AND not showing all: apply library limits filtering
906
    my $limited_reports = Koha::Reports->search_with_library_limits( {}, {}, C4::Context::mybranch() );
907
    my %allowed         = map  { $_->{id} => 1 } @{ $limited_reports->unblessed };
908
    my @limited         = grep { $allowed{ $_->{id} } } @$result;
909
910
    # Attach library limits data for the filtered list
911
    foreach my $report (@limited) {
912
        my $report_obj = Koha::Reports->find( $report->{id} );
913
        if ($report_obj) {
914
            my $limits_rs = $report_obj->get_library_limits;
915
            if ($limits_rs) {
916
                my @branches =
917
                    map { { branchcode => $_->branchcode, branchname => $_->branchname } } $limits_rs->as_list;
918
                $report->{library_limits} = \@branches;
919
            } else {
920
                $report->{library_limits} = [];
921
            }
922
        } else {
923
            $report->{library_limits} = [];
924
        }
925
    }
926
927
    return \@limited;
928
882
}
929
}
883
930
884
=head2 get_column_type($column)
931
=head2 get_column_type($column)
(-)a/C4/UsageStats.pm (+1 lines)
Lines 317-322 sub _shared_preferences { Link Here
317
        autoMemberNum
317
        autoMemberNum
318
        BorrowerRenewalPeriodBase
318
        BorrowerRenewalPeriodBase
319
        EnableBorrowerFiles
319
        EnableBorrowerFiles
320
        LimitReportsByBranch
320
        EnhancedMessagingPreferences
321
        EnhancedMessagingPreferences
321
        ExtendedPatronAttributes
322
        ExtendedPatronAttributes
322
        intranetreadinghistory
323
        intranetreadinghistory
(-)a/Koha/Manual.pm (+1 lines)
Lines 155-160 our $mapping = { Link Here
155
    'admin/preferences#logs'                   => '/logspreferences.html',
155
    'admin/preferences#logs'                   => '/logspreferences.html',
156
    'admin/preferences#opac'                   => '/opacpreferences.html',
156
    'admin/preferences#opac'                   => '/opacpreferences.html',
157
    'admin/preferences#patrons'                => '/patronspreferences.html',
157
    'admin/preferences#patrons'                => '/patronspreferences.html',
158
    'admin/preferences#reports'                => '/reportspreferences.html',
158
    'admin/preferences#searching'              => '/searchingpreferences.html',
159
    'admin/preferences#searching'              => '/searchingpreferences.html',
159
    'admin/preferences#serials'                => '/serialspreferences.html',
160
    'admin/preferences#serials'                => '/serialspreferences.html',
160
    'admin/preferences#staff_interface'        => '/staffclientpreferences.html',
161
    'admin/preferences#staff_interface'        => '/staffclientpreferences.html',
(-)a/Koha/Report.pm (-1 / +60 lines)
Lines 20-28 use Modern::Perl; Link Here
20
use Koha::Database;
20
use Koha::Database;
21
use Koha::Reports;
21
use Koha::Reports;
22
22
23
use Koha::Object;
24
use Koha::Object::Limit::Library;
25
use Koha::Patrons;
26
use C4::Context;
27
28
use base qw(Koha::Object Koha::Object::Limit::Library);
29
23
#use Koha::DateUtils qw( dt_from_string output_pref );
30
#use Koha::DateUtils qw( dt_from_string output_pref );
24
31
25
use base qw(Koha::Object);
26
#
32
#
27
# FIXME We could only return an error code instead of the arrayref
33
# FIXME We could only return an error code instead of the arrayref
28
# Only 1 error is returned
34
# Only 1 error is returned
Lines 268-271 sub _type { Link Here
268
    return 'SavedSql';
274
    return 'SavedSql';
269
}
275
}
270
276
277
=head3 _library_limits
278
279
Configurable library limits
280
281
=cut
282
283
sub _library_limits {
284
    return {
285
        class   => "ReportsBranch",
286
        id      => "report_id",
287
        library => "branchcode",
288
    };
289
}
290
291
=head3 can_manage_limits
292
293
    my $can = Koha::Report->can_manage_limits($patron);
294
295
Returns true if branch limiting is enabled and the patron can manage report limits (superlibrarian or permission reports => manage_report_limits).
296
297
=cut
298
299
sub can_manage_limits {
300
    my ( $class, $patron ) = @_;
301
    return C4::Context->preference('LimitReportsByBranch')
302
        && ( $patron->is_superlibrarian || $patron->has_permission( { reports => 'manage_report_limits' } ) );
303
}
304
305
=head3 can_access
306
307
    my $allowed = $report->can_access($patron);
308
309
Returns true if the patron may access this report considering branch limits, or if limits are disabled.
310
311
=cut
312
313
sub can_access {
314
    my ( $self, $patron ) = @_;
315
316
    return 1 unless C4::Context->preference('LimitReportsByBranch');
317
318
    return 1 if __PACKAGE__->can_manage_limits($patron);
319
320
    my $limits_rs = $self->get_library_limits;
321
    return 1 unless $limits_rs;
322
323
    my $user_branch = C4::Context::mybranch();
324
    return 0 unless $user_branch;
325
326
    my %allowed = map { $_->branchcode => 1 } $limits_rs->as_list;
327
    return $allowed{$user_branch} ? 1 : 0;
328
}
329
271
1;
330
1;
(-)a/Koha/Reports.pm (-7 / +1 lines)
Lines 21-27 use Koha::Database; Link Here
21
21
22
use Koha::Report;
22
use Koha::Report;
23
23
24
use base qw(Koha::Objects);
24
use base qw(Koha::Objects Koha::Objects::Limit::Library);
25
25
26
=head1 NAME
26
=head1 NAME
27
27
Lines 33-44 Koha::Reports - Koha Report Object set class Link Here
33
33
34
=cut
34
=cut
35
35
36
=head3 _type
37
38
Returns name of corresponding DBIC resultset
39
40
=cut
41
42
sub _type {
36
sub _type {
43
    return 'SavedSql';
37
    return 'SavedSql';
44
}
38
}
(-)a/admin/columns_settings.yml (+3 lines)
Lines 1667-1672 modules: Link Here
1667
              columnname: saved_results
1667
              columnname: saved_results
1668
            -
1668
            -
1669
              columnname: update
1669
              columnname: update
1670
            -
1671
              columnname: library_limits
1672
              is_hidden: 1
1670
            -
1673
            -
1671
              columnname: actions
1674
              columnname: actions
1672
              cannot_be_toggled: 1
1675
              cannot_be_toggled: 1
(-)a/installer/data/mysql/atomicupdate/bug_16631.pl (+39 lines)
Line 0 Link Here
1
use Modern::Perl;
2
use Koha::Installer::Output qw(say_warning say_success say_info);
3
4
return {
5
    bug_number  => "16631",
6
    description => "Add library limits to reports",
7
    up          => sub {
8
        my ($args) = @_;
9
        my ( $dbh, $out ) = @$args{qw(dbh out)};
10
11
        $dbh->do(
12
            q{CREATE TABLE IF NOT EXISTS `reports_branches` (
13
            `report_id` int(11) NOT NULL,
14
            `branchcode` varchar(10) NOT NULL,
15
            KEY `report_id` (`report_id`),
16
            KEY `branchcode` (`branchcode`),
17
            CONSTRAINT `reports_branches_ibfk_1` FOREIGN KEY (`report_id`) REFERENCES `saved_sql` (`id`) ON DELETE CASCADE,
18
            CONSTRAINT `reports_branches_ibfk_2` FOREIGN KEY (`branchcode`) REFERENCES `branches` (`branchcode`) ON DELETE CASCADE
19
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci}
20
        );
21
22
        say $out "Added new table 'reports_branches'";
23
24
        $dbh->do(
25
            q{INSERT IGNORE INTO systempreferences
26
            (variable,value,options,explanation,type) VALUES ('LimitReportsByBranch', '0', NULL, 'Enable ability for staff to limit report access based on home library.', 'YesNo')}
27
        );
28
29
        say $out "Added new system preference 'LimitReportsByBranch'";
30
31
        $dbh->do(
32
            q{INSERT IGNORE INTO permissions
33
            (module_bit, code, description) VALUES (16, 'manage_report_limits', 'Manage report limits')}
34
        );
35
36
        say $out "Added new permission 'manage_report_limits'";
37
38
    },
39
};
(-)a/installer/data/mysql/kohastructure.sql (+13 lines)
Lines 5871-5876 CREATE TABLE `saved_reports` ( Link Here
5871
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
5871
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
5872
/*!40101 SET character_set_client = @saved_cs_client */;
5872
/*!40101 SET character_set_client = @saved_cs_client */;
5873
5873
5874
DROP TABLE IF EXISTS `reports_branches`;
5875
/*!40101 SET @saved_cs_client     = @@character_set_client */;
5876
/*!40101 SET character_set_client = utf8mb4 */;
5877
CREATE TABLE `reports_branches` (
5878
  `report_id` int(11) NOT NULL,
5879
  `branchcode` varchar(10) NOT NULL,
5880
  KEY `report_id` (`report_id`),
5881
  KEY `branchcode` (`branchcode`),
5882
  CONSTRAINT `reports_branches_ibfk_1` FOREIGN KEY (`report_id`) REFERENCES `saved_sql` (`id`) ON DELETE CASCADE,
5883
  CONSTRAINT `reports_branches_ibfk_2` FOREIGN KEY (`branchcode`) REFERENCES `branches` (`branchcode`) ON DELETE CASCADE
5884
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
5885
/*!40101 SET character_set_client = @saved_cs_client */;
5886
5874
--
5887
--
5875
-- Table structure for table `saved_sql`
5888
-- Table structure for table `saved_sql`
5876
--
5889
--
(-)a/installer/data/mysql/mandatory/userpermissions.sql (+1 lines)
Lines 147-152 INSERT INTO permissions (module_bit, code, description) VALUES Link Here
147
   (16, 'execute_reports', 'Execute SQL reports'),
147
   (16, 'execute_reports', 'Execute SQL reports'),
148
   (16, 'create_reports', 'Create SQL reports'),
148
   (16, 'create_reports', 'Create SQL reports'),
149
   (16, 'delete_reports', 'Delete SQL reports'),
149
   (16, 'delete_reports', 'Delete SQL reports'),
150
   (16, 'manage_report_limits', 'Manage report limits'),
150
   (18, 'manage_courses', 'Add, edit and delete courses'),
151
   (18, 'manage_courses', 'Add, edit and delete courses'),
151
   (18, 'add_reserves', 'Add course reserves'),
152
   (18, 'add_reserves', 'Add course reserves'),
152
   (18, 'delete_reserves', 'Remove course reserves'),
153
   (18, 'delete_reserves', 'Remove course reserves'),
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc (+3 lines)
Lines 511-516 Link Here
511
    [%- CASE 'execute_reports' -%]
511
    [%- CASE 'execute_reports' -%]
512
        <span class="sub_permission execute_reports_subpermission"> Execute SQL reports </span>
512
        <span class="sub_permission execute_reports_subpermission"> Execute SQL reports </span>
513
        <span class="permissioncode">([% name | html %])</span>
513
        <span class="permissioncode">([% name | html %])</span>
514
    [%- CASE 'manage_report_limits' -%]
515
        <span class="sub_permission manage_report_limits_subpermission"> Manage Report Limits </span>
516
        <span class="permissioncode">([% name | html %])</span>
514
    [%- CASE 'add_reserves' -%]
517
    [%- CASE 'add_reserves' -%]
515
        <span class="sub_permission add_reserves_subpermission"> Add course reserves </span>
518
        <span class="sub_permission add_reserves_subpermission"> Add course reserves </span>
516
        <span class="permissioncode">([% name | html %])</span>
519
        <span class="permissioncode">([% name | html %])</span>
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/prefs-menu.inc (+11 lines)
Lines 165-170 Link Here
165
            </li>
165
            </li>
166
        [% END %]
166
        [% END %]
167
167
168
        [% IF ( reports ) %]
169
            <li class="active">
170
                <a title="Reports" href="/cgi-bin/koha/admin/preferences.pl?tab=reports">Reports</a>
171
                [% PROCESS subtabs %]
172
            </li>
173
        [% ELSE %]
174
            <li>
175
                <a title="Reports" href="/cgi-bin/koha/admin/preferences.pl?tab=reports">Reports</a>
176
            </li>
177
        [% END %]
178
168
        [% IF ( searching ) %]
179
        [% IF ( searching ) %]
169
            <li class="active">
180
            <li class="active">
170
                <a title="Searching" href="/cgi-bin/koha/admin/preferences.pl?tab=searching">Searching</a>
181
                <a title="Searching" href="/cgi-bin/koha/admin/preferences.pl?tab=searching">Searching</a>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/reports.pref (+8 lines)
Line 0 Link Here
1
Reports:
2
    Reports Access:
3
     -
4
         - pref: LimitReportsByBranch
5
           choices:
6
               1: Enable
7
               0: Disable
8
         - "limiting report access based on staff home library."
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/reports/guided_reports_start.tt (-3 / +112 lines)
Lines 3-8 Link Here
3
[% USE AuthorisedValues %]
3
[% USE AuthorisedValues %]
4
[% USE KohaDates %]
4
[% USE KohaDates %]
5
[% USE Koha %]
5
[% USE Koha %]
6
[% USE Branches %]
6
[% USE TablesSettings %]
7
[% USE TablesSettings %]
7
[% USE HtmlScrubber %]
8
[% USE HtmlScrubber %]
8
[% USE JSON.Escape %]
9
[% USE JSON.Escape %]
Lines 229-234 Link Here
229
230
230
                [% INCLUDE "reports-toolbar.inc" %]
231
                [% INCLUDE "reports-toolbar.inc" %]
231
232
233
                [% IF access_denied %]
234
                    <div>
235
                        <h1>Access denied</h1>
236
                        <p>This report is restricted and cannot be accessed by the current user.</p>
237
                        [% IF id %]<p>Report ID: [% id | html %]</p>[% END %]
238
                        <p><a class="btn btn-secondary" href="/cgi-bin/koha/reports/guided_reports.pl?op=list">Return to saved reports</a></p>
239
                    </div>
240
                [% END %]
241
232
                [% IF ( start ) %]
242
                [% IF ( start ) %]
233
                    <h1>Guided reports</h1>
243
                    <h1>Guided reports</h1>
234
                    <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>
244
                    <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>
Lines 264-270 Link Here
264
                    <h1>Saved reports</h1>
274
                    <h1>Saved reports</h1>
265
                    [% IF ( savedreports ) %]
275
                    [% IF ( savedreports ) %]
266
276
267
                        [% IF ( filters.date || filters.author || filters.keyword ) %]
277
                        [% IF ( filters.date || filters.author || filters.keyword || branch_limit_active ) %]
268
                            <p
278
                            <p
269
                                >Filtered by:
279
                                >Filtered by:
270
                                <span class="filter">
280
                                <span class="filter">
Lines 277-282 Link Here
277
                                    [% IF ( filters.keyword ) %]
287
                                    [% IF ( filters.keyword ) %]
278
                                        <span class="filter_keyword"><strong>Keyword:</strong> [% filters.keyword | html %]</span>
288
                                        <span class="filter_keyword"><strong>Keyword:</strong> [% filters.keyword | html %]</span>
279
                                    [% END %]
289
                                    [% END %]
290
                                    [% IF branch_limit_active %]
291
                                        <span class="filter_show_all"><strong>Show reports:</strong> My branch reports</span>
292
                                    [% END %]
280
                                    <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>
293
                                    <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>
281
                                </span>
294
                                </span>
282
                            </p>
295
                            </p>
Lines 337-342 Link Here
337
                                                    [% ELSE %]
350
                                                    [% ELSE %]
338
                                                        <th class="NoVisible">Update</th>
351
                                                        <th class="NoVisible">Update</th>
339
                                                    [% END %]
352
                                                    [% END %]
353
                                                    [% IF ( Koha.Preference('LimitReportsByBranch') && ( CAN_user_superlibrarian || CAN_user_reports_manage_report_limits ) ) %]
354
                                                        <th>Library limits</th>
355
                                                    [% END %]
340
                                                    <th class="no-sort no-export">Actions</th>
356
                                                    <th class="no-sort no-export">Actions</th>
341
                                                </tr>
357
                                                </tr>
342
                                            </thead>
358
                                            </thead>
Lines 404-409 Link Here
404
                                                                >
420
                                                                >
405
                                                            [% END %]
421
                                                            [% END %]
406
                                                        </td>
422
                                                        </td>
423
                                                        [% IF ( Koha.Preference('LimitReportsByBranch') && ( CAN_user_superlibrarian || CAN_user_reports_manage_report_limits ) ) %]
424
                                                            <td>
425
                                                                [% IF savedreport.library_limits && savedreport.library_limits.size > 0 %]
426
                                                                    [% library_str = "" %]
427
                                                                    [% FOREACH library IN savedreport.library_limits %]
428
                                                                        [%- IF loop.first -%]
429
                                                                            [% library_str = library.branchname _ " (" _ library.branchcode _ ")" %]
430
                                                                        [% ELSE %]
431
                                                                            [% library_str = library_str _ "\n" _ library.branchname _ " (" _ library.branchcode _ ")" %]
432
                                                                        [% END %]
433
                                                                    [% END %]
434
                                                                    <span class="library_limitation" data-bs-toggle="tooltip" title="[% library_str | html %]">
435
                                                                        [% IF savedreport.library_limits.size > 1 %]
436
                                                                            <span>[% savedreport.library_limits.size | html %] library limitations</span>
437
                                                                        [% ELSE %]
438
                                                                            <span>[% savedreport.library_limits.size | html %] library limitation</span>
439
                                                                        [% END %]
440
                                                                    </span>
441
                                                                [% ELSE %]
442
                                                                    <span>No limitation</span>
443
                                                                [% END %]
444
                                                            </td>
445
                                                        [% END %]
407
                                                        <td>
446
                                                        <td>
408
                                                            <div class="btn-group dropup">
447
                                                            <div class="btn-group dropup">
409
                                                                [%# There should be no space between these two buttons, it would render badly %]
448
                                                                [%# There should be no space between these two buttons, it would render badly %]
Lines 483-489 Link Here
483
522
484
                                    <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>
523
                                    <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>
485
524
486
                                    <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>
525
                                    <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>
487
                                [% END %]
526
                                [% END %]
488
                            [% ELSE %]
527
                            [% ELSE %]
489
                                <h4>There are no saved reports. </h4>
528
                                <h4>There are no saved reports. </h4>
Lines 879-884 Link Here
879
                            <input type="hidden" name="reportname" value="[% reportname | html %]" />
918
                            <input type="hidden" name="reportname" value="[% reportname | html %]" />
880
                            <input type="hidden" name="group" value="[% group | html %]" />
919
                            <input type="hidden" name="group" value="[% group | html %]" />
881
                            <input type="hidden" name="subgroup" value="[% subgroup | html %]" />
920
                            <input type="hidden" name="subgroup" value="[% subgroup | html %]" />
921
                            <input type="hidden" name="branches" value="[% branches | html %]" />
882
                            <input type="hidden" name="notes" value="[% notes | scrub_html type => 'note' | $raw %]" />
922
                            <input type="hidden" name="notes" value="[% notes | scrub_html type => 'note' | $raw %]" />
883
                            <input type="hidden" name="cache_expiry" value="[% cache_expiry | html %]" />
923
                            <input type="hidden" name="cache_expiry" value="[% cache_expiry | html %]" />
884
                            <input type="hidden" name="cache_expiry_units" value="[% cache_expiry_units | html %]" />
924
                            <input type="hidden" name="cache_expiry_units" value="[% cache_expiry_units | html %]" />
Lines 1475-1480 Link Here
1475
                        </fieldset>
1515
                        </fieldset>
1476
                        <!-- /.rows -->
1516
                        <!-- /.rows -->
1477
1517
1518
                        [% IF ( Koha.Preference('LimitReportsByBranch') && ( CAN_user_superlibrarian || CAN_user_reports_manage_report_limits ) ) %]
1519
                            <fieldset class="rows">
1520
                                <legend>Library limitation:</legend>
1521
                                <div>
1522
                                    <label for="library_limitation">Library limitation: </label>
1523
                                    <select id="library_limitation" name="branches" multiple size="10">
1524
                                        [% PROCESS options_for_libraries libraries => Branches.all( selected => report.get_library_limits, unfiltered => 1, do_not_select_my_library => 1 ) %]
1525
                                    </select>
1526
                                    <div class="hint">Limits the use of this report to the selected libraries.</div>
1527
                                </div>
1528
                            </fieldset>
1529
                        [% END %]
1530
1478
                        <fieldset class="rows">
1531
                        <fieldset class="rows">
1479
                            <legend>SQL:</legend>
1532
                            <legend>SQL:</legend>
1480
                            <div style="margin:1em;">
1533
                            <div style="margin:1em;">
Lines 1595-1600 Link Here
1595
                        </fieldset>
1648
                        </fieldset>
1596
                        <!-- /.rows -->
1649
                        <!-- /.rows -->
1597
1650
1651
                        [% IF ( Koha.Preference('LimitReportsByBranch') && ( CAN_user_superlibrarian || CAN_user_reports_manage_report_limits ) ) %]
1652
                            <fieldset class="rows">
1653
                                <legend>Library limitation:</legend>
1654
                                <div
1655
                                    ><label for="library_limitation">Library limitation: </label>
1656
                                    <select id="library_limitation" name="branches" multiple size="10">
1657
                                        [% PROCESS options_for_libraries libraries => Branches.all( selected => report.get_library_limits, unfiltered => 1, do_not_select_my_library => 1 ) %]
1658
                                    </select>
1659
                                    <div class="hint">Limits the use of this report to the selected libraries.</div>
1660
                                </div>
1661
                            </fieldset>
1662
                        [% END %]
1663
1598
                        <fieldset class="rows">
1664
                        <fieldset class="rows">
1599
                            <legend>SQL:</legend>
1665
                            <legend>SQL:</legend>
1600
                            [% PROCESS insert_runtime_parameter %]
1666
                            [% PROCESS insert_runtime_parameter %]
Lines 1671-1676 Link Here
1671
                                        <label for="filter_keyword">Keyword:</label>
1737
                                        <label for="filter_keyword">Keyword:</label>
1672
                                        <input type="text" id="filter_keyword" name="filter_keyword" value="[% filters.keyword | html %]" size="16" />
1738
                                        <input type="text" id="filter_keyword" name="filter_keyword" value="[% filters.keyword | html %]" size="16" />
1673
                                    </li>
1739
                                    </li>
1740
                                    <li>
1741
                                        [% IF Koha.Preference('LimitReportsByBranch') %]
1742
                                            [% IF ( CAN_user_superlibrarian || CAN_user_reports_manage_report_limits ) %]
1743
                                                <label for="filter_show_all_reports">Show reports:</label>
1744
                                                <select name="filter_show_all_reports" id="filter_show_all_reports">
1745
                                                    [% IF filters.show_all_reports == "1" %]
1746
                                                        <option value="1" selected="selected">Show all reports</option>
1747
                                                    [% ELSE %]
1748
                                                        <option value="1">Show all reports</option>
1749
                                                    [% END %]
1750
1751
                                                    [% IF filters.show_all_reports == 0 %]
1752
                                                        <option value="0" selected="selected">Show my branch reports</option>
1753
                                                    [% ELSE %]
1754
                                                        <option value="0">Show my branch reports</option>
1755
                                                    [% END %]
1756
                                                </select>
1757
                                            [% END %]
1758
                                        [% END %]
1759
                                    </li>
1674
                                </ol>
1760
                                </ol>
1675
                            </fieldset>
1761
                            </fieldset>
1676
                            <!-- /.brief -->
1762
                            <!-- /.brief -->
Lines 2211-2216 Link Here
2211
2297
2212
            [% IF (saved1) %]
2298
            [% IF (saved1) %]
2213
                var table_settings = [% TablesSettings.GetTableSettings( 'reports', 'saved-sql', 'table_reports', 'json' ) | $raw %];
2299
                var table_settings = [% TablesSettings.GetTableSettings( 'reports', 'saved-sql', 'table_reports', 'json' ) | $raw %];
2300
                [% UNLESS ( Koha.Preference('LimitReportsByBranch') && ( CAN_user_superlibrarian || CAN_user_reports_manage_report_limits ) ) %]
2301
                  table_settings.columns = table_settings.columns.filter(function(col) {
2302
                      return col.columnname !== 'library_limits';
2303
                  });
2304
                [% END %]
2305
2306
                [% IF ( Koha.Preference('LimitReportsByBranch') && ( CAN_user_superlibrarian || CAN_user_reports_manage_report_limits ) ) %]
2307
                  let library_limits_col_index = -1;
2308
                  table_settings.columns.forEach(function(col, index) {
2309
                      if (col.columnname === 'library_limits') {
2310
                          library_limits_col_index = index;
2311
                      }
2312
                  });
2313
                [% END %]
2314
2214
                var rtable = $("#table_reports").kohaTable(
2315
                var rtable = $("#table_reports").kohaTable(
2215
                    {
2316
                    {
2216
                        autoWidth: false,
2317
                        autoWidth: false,
Lines 2218-2224 Link Here
2218
                        order: [[1, "asc"]],
2319
                        order: [[1, "asc"]],
2219
                        language: {
2320
                        language: {
2220
                            zeroRecords: _("No matching reports found"),
2321
                            zeroRecords: _("No matching reports found"),
2221
                        },
2322
                        }[% IF ( Koha.Preference('LimitReportsByBranch') && ( CAN_user_superlibrarian || CAN_user_reports_manage_report_limits ) ) %],
2323
                        columnDefs: library_limits_col_index !== -1 ? [{ visible: false, targets: [library_limits_col_index] }] : []
2324
                        [% END %]
2222
                    },
2325
                    },
2223
                    table_settings
2326
                    table_settings
2224
                );
2327
                );
Lines 2511-2516 Link Here
2511
                selectField.style.minWidth = '320px';
2614
                selectField.style.minWidth = '320px';
2512
                $(selectField).select2();
2615
                $(selectField).select2();
2513
            });
2616
            });
2617
2618
            const libraryLimitSelectFields = document.querySelectorAll('#library_limitation');
2619
            libraryLimitSelectFields.forEach((selectField) => {
2620
                selectField.style.minWidth = '450px';
2621
                $(selectField).select2();
2622
            });
2514
        });
2623
        });
2515
2624
2516
        $("#toggle_auto_links").on("click", function(e){
2625
        $("#toggle_auto_links").on("click", function(e){
(-)a/reports/guided_reports.pl (-248 / +307 lines)
Lines 83-101 my ( $template, $borrowernumber, $cookie ) = get_template_and_user( Link Here
83
my $session_id = $input->cookie('CGISESSID');
83
my $session_id = $input->cookie('CGISESSID');
84
my $session    = $session_id ? get_session($session_id) : undef;
84
my $session    = $session_id ? get_session($session_id) : undef;
85
85
86
$template->param( templates => Koha::Notice::Templates->search( { module => 'report' } ) );
87
88
my $filter;
86
my $filter;
89
if ( $input->param("filter_set") or $input->param('clear_filters') ) {
87
if ( $input->param("filter_set") or $input->param('clear_filters') ) {
90
    $filter = {};
88
    $filter = {};
91
    $filter->{$_} = $input->param("filter_$_") foreach qw/date author keyword group subgroup/;
89
    $filter->{$_} = $input->param("filter_$_") foreach qw/date author keyword show_all_reports group subgroup/;
90
91
    if ( $input->param('clear_filters') ) {
92
        $filter->{show_all_reports} = 1;
93
    }
94
92
    $session->param( 'report_filter', $filter ) if $session;
95
    $session->param( 'report_filter', $filter ) if $session;
93
    $template->param( 'filter_set' => 1 );
96
    $template->param( 'filter_set' => 1 );
94
} elsif ( $session and not $input->param('clear_filters') ) {
97
} elsif ( $session and not $input->param('clear_filters') ) {
95
    $filter = $session->param('report_filter');
98
    $filter = $session->param('report_filter');
96
}
99
}
97
100
98
my @errors = ();
101
my @errors             = ();
102
my %OP_REQUIRES_REPORT = map { $_ => 1 } qw(show edit_form duplicate cud-update_sql cud-update_and_run_sql export run);
103
my $report_id          = $input->param('id');
104
my $report;
105
my $access_blocked;
106
if ( $OP_REQUIRES_REPORT{$op} && $report_id ) {
107
    $report = Koha::Reports->find($report_id);
108
    if ($report) {
109
        my $patron = Koha::Patrons->find($borrowernumber);
110
        unless ( $report->can_access($patron) ) {
111
            $template->param( access_denied => 1, denied_op => $op, id => $report_id );
112
            $access_blocked = 1;
113
        }
114
    } else {
115
        push @errors, { no_sql_for_id => $report_id };
116
    }
117
}
118
119
if ($access_blocked) {
120
    output_html_with_http_headers $input, $cookie, $template->output;
121
    exit;
122
}
123
99
if ( !$op ) {
124
if ( !$op ) {
100
    $template->param( 'start' => 1 );
125
    $template->param( 'start' => 1 );
101
126
Lines 116-262 if ( !$op ) { Link Here
116
    $op = 'list';
141
    $op = 'list';
117
142
118
} elsif ( $op eq 'show' ) {
143
} elsif ( $op eq 'show' ) {
119
144
    if ($report) {
120
    my $id     = $input->param('id');
145
        $template->param(
121
    my $report = Koha::Reports->find($id);
146
            'id'            => $report_id,
122
    $template->param(
147
            'reportname'    => $report->report_name,
123
        'id'            => $id,
148
            'notes'         => $report->notes,
124
        'reportname'    => $report->report_name,
149
            'sql'           => $report->savedsql,
125
        'notes'         => $report->notes,
150
            'showsql'       => 1,
126
        'sql'           => $report->savedsql,
151
            'mana_success'  => scalar $input->param('mana_success'),
127
        'showsql'       => 1,
152
            'mana_id'       => $report->{mana_id},
128
        'mana_success'  => scalar $input->param('mana_success'),
153
            'mana_comments' => $report->{comments}
129
        'mana_id'       => $report->{mana_id},
154
        );
130
        'mana_comments' => $report->{comments}
155
    }
131
    );
132
156
133
} elsif ( $op eq 'edit_form' ) {
157
} elsif ( $op eq 'edit_form' ) {
134
    my $id       = $input->param('id');
158
    if ($report) {
135
    my $report   = Koha::Reports->find($id);
159
        my $group    = $report->report_group;
136
    my $group    = $report->report_group;
160
        my $subgroup = $report->report_subgroup;
137
    my $subgroup = $report->report_subgroup;
161
        my $tables   = get_tables();
138
    my $tables   = get_tables();
162
        $template->param(
139
    $template->param(
163
            'sql'                   => $report->savedsql,
140
        'sql'                   => $report->savedsql,
164
            'reportname'            => $report->report_name,
141
        'reportname'            => $report->report_name,
165
            'groups_with_subgroups' => groups_with_subgroups( $group, $subgroup ),
142
        'groups_with_subgroups' => groups_with_subgroups( $group, $subgroup ),
166
            'notes'                 => $report->notes,
143
        'notes'                 => $report->notes,
167
            'id'                    => $report_id,
144
        'id'                    => $id,
168
            'cache_expiry'          => $report->cache_expiry,
145
        'cache_expiry'          => $report->cache_expiry,
169
            'public'                => $report->public,
146
        'public'                => $report->public,
170
            'usecache'              => $usecache,
147
        'usecache'              => $usecache,
171
            'editsql'               => 1,
148
        'editsql'               => 1,
172
            'mana_id'               => $report->{mana_id},
149
        'mana_id'               => $report->{mana_id},
173
            'mana_comments'         => $report->{comments},
150
        'mana_comments'         => $report->{comments},
174
            'tables'                => $tables,
151
        'tables'                => $tables
175
            'report'                => $report,
152
    );
176
        );
177
    }
153
178
154
} elsif ( $op eq 'cud-update_sql' || $op eq 'cud-update_and_run_sql' ) {
179
} elsif ( $op eq 'cud-update_sql' || $op eq 'cud-update_and_run_sql' ) {
155
    my $id                 = $input->param('id');
180
    if ($report) {
156
    my $sql                = $input->param('sql');
181
        my $id                 = $report_id;
157
    my $reportname         = $input->param('reportname');
182
        my $sql                = $input->param('sql');
158
    my $group              = $input->param('group');
183
        my $reportname         = $input->param('reportname');
159
    my $subgroup           = $input->param('subgroup');
184
        my $group              = $input->param('group');
160
    my $notes              = $input->param('notes');
185
        my $subgroup           = $input->param('subgroup');
161
    my $cache_expiry       = $input->param('cache_expiry');
186
        my $notes              = $input->param('notes');
162
    my $cache_expiry_units = $input->param('cache_expiry_units');
187
        my $cache_expiry       = $input->param('cache_expiry');
163
    my $public             = $input->param('public');
188
        my $cache_expiry_units = $input->param('cache_expiry_units');
164
    my $save_anyway        = $input->param('save_anyway');
189
        my $public             = $input->param('public');
165
    my @errors;
190
        my $save_anyway        = $input->param('save_anyway');
166
    my $tables = get_tables();
191
        my @errors;
192
        my $tables   = get_tables();
193
        my @branches = grep { $_ ne q{} } $input->multi_param('branches');
194
195
        # if we have the units, then we came from creating a report from SQL and thus need to handle converting units
196
        if ($cache_expiry_units) {
197
            if ( $cache_expiry_units eq "minutes" ) {
198
                $cache_expiry *= 60;
199
            } elsif ( $cache_expiry_units eq "hours" ) {
200
                $cache_expiry *= 3600;    # 60 * 60
201
            } elsif ( $cache_expiry_units eq "days" ) {
202
                $cache_expiry *= 86400;    # 60 * 60 * 24
203
            }
204
        }
167
205
168
    # if we have the units, then we came from creating a report from SQL and thus need to handle converting units
206
        # 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
169
    if ($cache_expiry_units) {
207
        if ( $cache_expiry >= 2592000 ) {
170
        if ( $cache_expiry_units eq "minutes" ) {
208
            push @errors, { cache_expiry => $cache_expiry };
171
            $cache_expiry *= 60;
172
        } elsif ( $cache_expiry_units eq "hours" ) {
173
            $cache_expiry *= 3600;    # 60 * 60
174
        } elsif ( $cache_expiry_units eq "days" ) {
175
            $cache_expiry *= 86400;    # 60 * 60 * 24
176
        }
209
        }
177
    }
178
210
179
    # 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
211
        create_non_existing_group_and_subgroup( $input, $group, $subgroup );
180
    if ( $cache_expiry >= 2592000 ) {
181
        push @errors, { cache_expiry => $cache_expiry };
182
    }
183
212
184
    create_non_existing_group_and_subgroup( $input, $group, $subgroup );
213
        my ( $is_sql_valid, $validation_errors ) = Koha::Report->new( { savedsql => $sql } )->is_sql_valid;
214
        push( @errors, @$validation_errors ) unless $is_sql_valid;
185
215
186
    my ( $is_sql_valid, $validation_errors ) = Koha::Report->new( { savedsql => $sql } )->is_sql_valid;
216
        if (@errors) {
187
    push( @errors, @$validation_errors ) unless $is_sql_valid;
217
            $template->param(
218
                'errors' => \@errors,
219
                'sql'    => $sql,
220
            );
221
        } else {
188
222
189
    if (@errors) {
223
            # Check defined SQL parameters for authorised value validity
190
        $template->param(
224
            my $problematic_authvals = ValidateSQLParameters($sql);
191
            'errors' => \@errors,
192
            'sql'    => $sql,
193
        );
194
    } else {
195
225
196
        # Check defined SQL parameters for authorised value validity
226
            if ( scalar @$problematic_authvals > 0 && not $save_anyway ) {
197
        my $problematic_authvals = ValidateSQLParameters($sql);
198
227
199
        if ( scalar @$problematic_authvals > 0 && not $save_anyway ) {
228
                # There's at least one problematic parameter, report to the
229
                # GUI and provide all user input for further actions
230
                $template->param(
231
                    'id'                   => $id,
232
                    'sql'                  => $sql,
233
                    'reportname'           => $reportname,
234
                    'group'                => $group,
235
                    'subgroup'             => $subgroup,
236
                    'notes'                => $notes,
237
                    'public'               => $public,
238
                    'problematic_authvals' => $problematic_authvals,
239
                    'warn_authval_problem' => 1,
240
                    'phase_update'         => 1,
241
                    'branches'             => @branches,
242
                    'report'               => $report,
243
                );
200
244
201
            # There's at least one problematic parameter, report to the
245
            } else {
202
            # GUI and provide all user input for further actions
203
            $template->param(
204
                'id'                   => $id,
205
                'sql'                  => $sql,
206
                'reportname'           => $reportname,
207
                'group'                => $group,
208
                'subgroup'             => $subgroup,
209
                'notes'                => $notes,
210
                'public'               => $public,
211
                'problematic_authvals' => $problematic_authvals,
212
                'warn_authval_problem' => 1,
213
                'phase_update'         => 1,
214
            );
215
246
216
        } else {
247
                # No params problem found or asked to save anyway
248
                update_sql(
249
                    $id,
250
                    {
251
                        sql          => $sql,
252
                        name         => $reportname,
253
                        group        => $group,
254
                        subgroup     => $subgroup,
255
                        notes        => $notes,
256
                        public       => $public,
257
                        cache_expiry => $cache_expiry,
258
                    }
259
                );
217
260
218
            # No params problem found or asked to save anyway
261
                $report->store;
219
            update_sql(
262
                $report->replace_library_limits( \@branches );
220
                $id,
263
221
                {
264
                my $editsql = 1;
222
                    sql          => $sql,
265
                if ( $op eq 'cud-update_and_run_sql' ) {
223
                    name         => $reportname,
266
                    $editsql = 0;
224
                    group        => $group,
225
                    subgroup     => $subgroup,
226
                    notes        => $notes,
227
                    public       => $public,
228
                    cache_expiry => $cache_expiry,
229
                }
267
                }
230
            );
231
268
232
            my $editsql = 1;
269
                $template->param(
270
                    'save_successful'       => 1,
271
                    'reportname'            => $reportname,
272
                    'id'                    => $id,
273
                    'editsql'               => $editsql,
274
                    'sql'                   => $sql,
275
                    'groups_with_subgroups' => groups_with_subgroups( $group, $subgroup ),
276
                    'notes'                 => $notes,
277
                    'cache_expiry'          => $cache_expiry,
278
                    'public'                => $public,
279
                    'usecache'              => $usecache,
280
                    'tables'                => $tables,
281
                    'report'                => $report
282
                );
283
                logaction( "REPORTS", "MODIFY", $id, "$reportname | $sql" ) if C4::Context->preference("ReportsLog");
284
            }
285
            if ($usecache) {
286
                $template->param(
287
                    cache_expiry       => $cache_expiry,
288
                    cache_expiry_units => $cache_expiry_units,
289
                );
290
            }
233
            if ( $op eq 'cud-update_and_run_sql' ) {
291
            if ( $op eq 'cud-update_and_run_sql' ) {
234
                $editsql = 0;
292
                $op = 'run';
235
            }
293
            }
236
237
            $template->param(
238
                'save_successful'       => 1,
239
                'reportname'            => $reportname,
240
                'id'                    => $id,
241
                'editsql'               => $editsql,
242
                'sql'                   => $sql,
243
                'groups_with_subgroups' => groups_with_subgroups( $group, $subgroup ),
244
                'notes'                 => $notes,
245
                'cache_expiry'          => $cache_expiry,
246
                'public'                => $public,
247
                'usecache'              => $usecache,
248
                'tables'                => $tables
249
            );
250
            logaction( "REPORTS", "MODIFY", $id, "$reportname | $sql" ) if C4::Context->preference("ReportsLog");
251
        }
252
        if ($usecache) {
253
            $template->param(
254
                cache_expiry       => $cache_expiry,
255
                cache_expiry_units => $cache_expiry_units,
256
            );
257
        }
258
        if ( $op eq 'cud-update_and_run_sql' ) {
259
            $op = 'run';
260
        }
294
        }
261
    }
295
    }
262
296
Lines 514-519 if ( !$op ) { Link Here
514
    my $public             = $input->param('public');
548
    my $public             = $input->param('public');
515
    my $save_anyway        = $input->param('save_anyway');
549
    my $save_anyway        = $input->param('save_anyway');
516
    my $tables             = get_tables();
550
    my $tables             = get_tables();
551
    my @branches           = grep { $_ ne q{} } $input->multi_param('branches');
517
552
518
    # if we have the units, then we came from creating a report from SQL and thus need to handle converting units
553
    # if we have the units, then we came from creating a report from SQL and thus need to handle converting units
519
    if ($cache_expiry_units) {
554
    if ($cache_expiry_units) {
Lines 591-596 if ( !$op ) { Link Here
591
                    public         => $public,
626
                    public         => $public,
592
                }
627
                }
593
            );
628
            );
629
            my $report = Koha::Reports->find($id);
630
            $report->replace_library_limits( \@branches );
631
594
            logaction( "REPORTS", "ADD", $id, "$name | $sql" ) if C4::Context->preference("ReportsLog");
632
            logaction( "REPORTS", "ADD", $id, "$name | $sql" ) if C4::Context->preference("ReportsLog");
595
            $template->param(
633
            $template->param(
596
                'save_successful'       => 1,
634
                'save_successful'       => 1,
Lines 603-609 if ( !$op ) { Link Here
603
                'cache_expiry'          => $cache_expiry,
641
                'cache_expiry'          => $cache_expiry,
604
                'public'                => $public,
642
                'public'                => $public,
605
                'usecache'              => $usecache,
643
                'usecache'              => $usecache,
606
                'tables'                => $tables
644
                'tables'                => $tables,
645
                'report'                => $report,
607
            );
646
            );
608
        }
647
        }
609
    }
648
    }
Lines 621-765 if ( !$op ) { Link Here
621
} elsif ( $op eq 'export' ) {
660
} elsif ( $op eq 'export' ) {
622
661
623
    # export results to tab separated text or CSV
662
    # export results to tab separated text or CSV
624
    my $report_id   = $input->param('id');
663
    if ($report) {
625
    my $report      = Koha::Reports->find($report_id);
664
        my $sql         = $report->savedsql;
626
    my $sql         = $report->savedsql;
665
        my @param_names = $input->multi_param('param_name');
627
    my @param_names = $input->multi_param('param_name');
666
        my @sql_params  = $input->multi_param('sql_params');
628
    my @sql_params  = $input->multi_param('sql_params');
667
        my $format      = $input->param('format');
629
    my $format      = $input->param('format');
668
        my $reportname  = $input->param('reportname');
630
    my $reportname  = $input->param('reportname');
669
        my $reportfilename =
631
    my $reportfilename =
670
            $reportname ? "$report_id-$reportname-reportresults.$format" : "$report_id-reportresults.$format";
632
        $reportname ? "$report_id-$reportname-reportresults.$format" : "$report_id-reportresults.$format";
671
        my $scrubber = C4::Scrubber->new();
633
    my $scrubber = C4::Scrubber->new();
672
634
673
        ( $sql, undef ) = $report->prep_report( \@param_names, \@sql_params, { export => 1 } );
635
    ( $sql, undef ) = $report->prep_report( \@param_names, \@sql_params, { export => 1 } );
674
        my ( $sth, $q_errors ) = execute_query( { sql => $sql, report_id => $report_id } );
636
    my ( $sth, $q_errors ) = execute_query( { sql => $sql, report_id => $report_id } );
675
        unless ( $q_errors and @$q_errors ) {
637
    unless ( $q_errors and @$q_errors ) {
676
            my ( $type, $content );
638
        my ( $type, $content );
677
            if ( $format eq 'tab' ) {
639
        if ( $format eq 'tab' ) {
678
                $type = 'application/octet-stream';
640
            $type = 'application/octet-stream';
679
                $content .= join( "\t", header_cell_values($sth) ) . "\n";
641
            $content .= join( "\t", header_cell_values($sth) ) . "\n";
680
                $content = $scrubber->scrub( Encode::decode( 'UTF-8', $content ) );
642
            $content = $scrubber->scrub( Encode::decode( 'UTF-8', $content ) );
681
                while ( my $row = $sth->fetchrow_arrayref() ) {
643
            while ( my $row = $sth->fetchrow_arrayref() ) {
682
                    $content .= $scrubber->scrub( join( "\t", map { $_ // '' } @$row ) ) . "\n";
644
                $content .= $scrubber->scrub( join( "\t", map { $_ // '' } @$row ) ) . "\n";
683
                }
645
            }
684
            } else {
646
        } else {
685
                if ( $format eq 'csv' ) {
647
            if ( $format eq 'csv' ) {
686
                    my $delimiter = C4::Context->csv_delimiter;
648
                my $delimiter = C4::Context->csv_delimiter;
687
                    $type = 'application/csv';
649
                $type = 'application/csv';
650
651
                # Add BOM for UTF-8 encoded CSV
652
                $content .= "\xEF\xBB\xBF";
653
688
654
                my $csv =
689
                    # Add BOM for UTF-8 encoded CSV
655
                    Text::CSV::Encoded->new( { encoding_out => 'UTF-8', sep_char => $delimiter, formula => 'empty' } );
690
                    $content .= "\xEF\xBB\xBF";
656
                $csv or die "Text::CSV::Encoded->new({binary => 1}) FAILED: " . Text::CSV::Encoded->error_diag();
657
                if ( $csv->combine( header_cell_values($sth) ) ) {
658
                    $content .= $scrubber->scrub( Encode::decode( 'UTF-8', $csv->string() ) ) . "\n";
659
691
660
                } else {
692
                    my $csv =
661
                    push @$q_errors, { combine => 'HEADER ROW: ' . $csv->error_diag() };
693
                        Text::CSV::Encoded->new(
662
                }
694
                        { encoding_out => 'UTF-8', sep_char => $delimiter, formula => 'empty' } );
663
                while ( my $row = $sth->fetchrow_arrayref() ) {
695
                    $csv or die "Text::CSV::Encoded->new({binary => 1}) FAILED: " . Text::CSV::Encoded->error_diag();
664
                    if ( $csv->combine(@$row) ) {
696
                    if ( $csv->combine( header_cell_values($sth) ) ) {
665
                        $content .= $scrubber->scrub( $csv->string() ) . "\n";
697
                        $content .= $scrubber->scrub( Encode::decode( 'UTF-8', $csv->string() ) ) . "\n";
666
698
667
                    } else {
699
                    } else {
668
                        push @$q_errors, { combine => $csv->error_diag() };
700
                        push @$q_errors, { combine => 'HEADER ROW: ' . $csv->error_diag() };
669
                    }
701
                    }
670
                }
702
                    while ( my $row = $sth->fetchrow_arrayref() ) {
671
            } elsif ( $format eq 'ods' && C4::Context->preference('ReportsExportFormatODS') ) {
703
                        if ( $csv->combine(@$row) ) {
672
                $type = 'application/vnd.oasis.opendocument.spreadsheet';
704
                            $content .= $scrubber->scrub( $csv->string() ) . "\n";
673
                my $ods_fh       = File::Temp->new( UNLINK => 0 );
705
674
                my $ods_filepath = $ods_fh->filename;
706
                        } else {
675
                my $ods_content;
707
                            push @$q_errors, { combine => $csv->error_diag() };
676
708
                        }
677
                # First line is headers
709
                    }
678
                my @headers = header_cell_values($sth);
710
                } elsif ( $format eq 'ods' && C4::Context->preference('ReportsExportFormatODS') ) {
679
                push @$ods_content, \@headers;
711
                    $type = 'application/vnd.oasis.opendocument.spreadsheet';
680
712
                    my $ods_fh       = File::Temp->new( UNLINK => 0 );
681
                # Other line in Unicode
713
                    my $ods_filepath = $ods_fh->filename;
682
                my $sql_rows = $sth->fetchall_arrayref();
714
                    my $ods_content;
683
                foreach my $sql_row (@$sql_rows) {
715
684
                    my @content_row;
716
                    # First line is headers
685
                    foreach my $sql_cell (@$sql_row) {
717
                    my @headers = header_cell_values($sth);
686
                        push @content_row, $scrubber->scrub( Encode::encode( 'UTF8', $sql_cell ) );
718
                    push @$ods_content, \@headers;
719
720
                    # Other line in Unicode
721
                    my $sql_rows = $sth->fetchall_arrayref();
722
                    foreach my $sql_row (@$sql_rows) {
723
                        my @content_row;
724
                        foreach my $sql_cell (@$sql_row) {
725
                            push @content_row, $scrubber->scrub( Encode::encode( 'UTF8', $sql_cell ) );
687
726
727
                        }
728
                        push @$ods_content, \@content_row;
688
                    }
729
                    }
689
                    push @$ods_content, \@content_row;
690
                }
691
730
692
                # Process
731
                    # Process
693
                generate_ods( $ods_filepath, $ods_content );
732
                    generate_ods( $ods_filepath, $ods_content );
733
734
                    # Output
735
                    binmode(STDOUT);
736
                    open $ods_fh, '<', $ods_filepath;
737
                    print $input->header(
738
                        -type       => $type,
739
                        -attachment => $reportfilename
740
                    );
741
                    print $_ while <$ods_fh>;
742
                    unlink $ods_filepath;
743
                } elsif ( $format eq 'template' ) {
744
                    my $template_id     = $input->param('template');
745
                    my $notice_template = Koha::Notice::Templates->find($template_id);
746
                    my $data            = $sth->fetchall_arrayref( {} );
747
                    $content = process_tt(
748
                        $notice_template->content,
749
                        {
750
                            data         => $data,
751
                            report_id    => $report_id,
752
                            for_download => 1,
753
                        }
754
                    );
755
                    $reportfilename = process_tt(
756
                        $notice_template->title,
757
                        {
758
                            data      => $data,
759
                            report_id => $report_id,
760
                        }
761
                    );
762
                }
763
            }
694
764
695
                # Output
765
            unless ( $format eq 'ods' ) {
696
                binmode(STDOUT);
697
                open $ods_fh, '<', $ods_filepath;
698
                print $input->header(
766
                print $input->header(
699
                    -type       => $type,
767
                    -type       => $type,
700
                    -attachment => $reportfilename
768
                    -attachment => $reportfilename
701
                );
769
                );
702
                print $_ while <$ods_fh>;
770
                print $content;
703
                unlink $ods_filepath;
704
            } elsif ( $format eq 'template' ) {
705
                my $template_id     = $input->param('template');
706
                my $notice_template = Koha::Notice::Templates->find($template_id);
707
                my $data            = $sth->fetchall_arrayref( {} );
708
                $content = process_tt(
709
                    $notice_template->content,
710
                    {
711
                        data         => $data,
712
                        report_id    => $report_id,
713
                        for_download => 1,
714
                    }
715
                );
716
                $reportfilename = process_tt(
717
                    $notice_template->title,
718
                    {
719
                        data      => $data,
720
                        report_id => $report_id,
721
                    }
722
                );
723
            }
771
            }
724
        }
725
772
726
        unless ( $format eq 'ods' ) {
773
            foreach my $err ( @$q_errors, @errors ) {
727
            print $input->header(
774
                print "# ERROR: " . ( map { $_ . ": " . $err->{$_} } keys %$err ) . "\n";
728
                -type       => $type,
775
            }    # here we print all the non-fatal errors at the end.  Not super smooth, but better than nothing.
729
                -attachment => $reportfilename
776
            exit;
730
            );
731
            print $content;
732
        }
777
        }
733
778
        $template->param(
734
        foreach my $err ( @$q_errors, @errors ) {
779
            'sql'     => $sql,
735
            print "# ERROR: " . ( map { $_ . ": " . $err->{$_} } keys %$err ) . "\n";
780
            'execute' => 1,
736
        }    # here we print all the non-fatal errors at the end.  Not super smooth, but better than nothing.
781
            'name'    => 'Error exporting report!',
737
        exit;
782
            'notes'   => '',
783
            'errors'  => $q_errors,
784
        );
738
    }
785
    }
739
    $template->param(
740
        'sql'     => $sql,
741
        'execute' => 1,
742
        'name'    => 'Error exporting report!',
743
        'notes'   => '',
744
        'errors'  => $q_errors,
745
    );
746
786
747
} elsif ( $op eq 'add_form_sql' || $op eq 'duplicate' ) {
787
} elsif ( $op eq 'add_form_sql' || $op eq 'duplicate' ) {
748
788
749
    my ( $group, $subgroup, $sql, $reportname, $notes );
789
    my ( $group, $subgroup, $sql, $reportname, $notes, @branches, $report );
750
    if ( $input->param('sql') ) {
790
    if ( $input->param('sql') ) {
751
        $group      = $input->param('report_group');
791
        $group      = $input->param('report_group');
752
        $subgroup   = $input->param('report_subgroup');
792
        $subgroup   = $input->param('report_subgroup');
753
        $sql        = $input->param('sql')        // '';
793
        $sql        = $input->param('sql')        // '';
754
        $reportname = $input->param('reportname') // '';
794
        $reportname = $input->param('reportname') // '';
755
        $notes      = $input->param('notes')      // '';
795
        $notes      = $input->param('notes')      // '';
796
        @branches   = grep { $_ ne q{} } $input->multi_param('branches');
797
756
    } elsif ( my $report_id = $input->param('id') ) {
798
    } elsif ( my $report_id = $input->param('id') ) {
757
        my $report = Koha::Reports->find($report_id);
799
        $report     = Koha::Reports->find($report_id);
758
        $group      = $report->report_group;
800
        $group      = $report->report_group;
759
        $subgroup   = $report->report_subgroup;
801
        $subgroup   = $report->report_subgroup;
760
        $sql        = $report->savedsql    // '';
802
        $sql        = $report->savedsql    // '';
761
        $reportname = $report->report_name // '';
803
        $reportname = $report->report_name // '';
762
        $notes      = $report->notes       // '';
804
        $notes      = $report->notes       // '';
805
        @branches   = grep { $_ ne q{} } $input->multi_param('branches');
806
763
    }
807
    }
764
808
765
    my $tables = get_tables();
809
    my $tables = get_tables();
Lines 774-779 if ( !$op ) { Link Here
774
        'cache_expiry'          => 300,
818
        'cache_expiry'          => 300,
775
        'usecache'              => $usecache,
819
        'usecache'              => $usecache,
776
        'tables'                => $tables,
820
        'tables'                => $tables,
821
        'report'                => $report,
777
822
778
    );
823
    );
779
}
824
}
Lines 783-789 if ( $op eq 'run' ) { Link Here
783
    # execute a saved report
828
    # execute a saved report
784
    my $limit           = $input->param('limit') || 20;
829
    my $limit           = $input->param('limit') || 20;
785
    my $offset          = 0;
830
    my $offset          = 0;
786
    my $report_id       = $input->param('id');
787
    my @sql_params      = $input->multi_param('sql_params');
831
    my @sql_params      = $input->multi_param('sql_params');
788
    my @param_names     = $input->multi_param('param_name');
832
    my @param_names     = $input->multi_param('param_name');
789
    my $template_id     = $input->param('template');
833
    my $template_id     = $input->param('template');
Lines 800-806 if ( $op eq 'run' ) { Link Here
800
    );
844
    );
801
845
802
    my ( $sql, $original_sql, $type, $name, $notes );
846
    my ( $sql, $original_sql, $type, $name, $notes );
803
    if ( my $report = Koha::Reports->find($report_id) ) {
847
    if ($report) {
804
        $sql   = $original_sql = $report->savedsql;
848
        $sql   = $original_sql = $report->savedsql;
805
        $name  = $report->report_name;
849
        $name  = $report->report_name;
806
        $notes = $report->notes;
850
        $notes = $report->notes;
Lines 1047-1054 if ( $op eq 'run' ) { Link Here
1047
                'param_names' => \@param_names,
1091
                'param_names' => \@param_names,
1048
            );
1092
            );
1049
        }
1093
        }
1050
    } else {
1051
        push @errors, { no_sql_for_id => $report_id };
1052
    }
1094
    }
1053
}
1095
}
1054
1096
Lines 1077-1087 if ( $op eq 'list' || $op eq 'convert' ) { Link Here
1077
1119
1078
    # use a saved report
1120
    # use a saved report
1079
    # get list of reports and display them
1121
    # get list of reports and display them
1080
    my $group    = $input->param('group');
1122
    my $group             = $input->param('group');
1081
    my $subgroup = $input->param('subgroup');
1123
    my $subgroup          = $input->param('subgroup');
1124
    my $show_all_filter   = $filter->{show_all_reports};
1125
    my $logged_in_user    = Koha::Patrons->find($borrowernumber);
1126
    my $can_manage_limits = Koha::Report->can_manage_limits($logged_in_user);
1082
    $filter->{group}    = $group;
1127
    $filter->{group}    = $group;
1083
    $filter->{subgroup} = $subgroup;
1128
    $filter->{subgroup} = $subgroup;
1129
1130
    if ($show_all_filter) {
1131
        $filter->{show_all_reports} = $can_manage_limits ? $show_all_filter : 0;
1132
    } else {
1133
        $filter->{show_all_reports} = 0;
1134
    }
1135
1084
    my $reports = get_saved_reports($filter);
1136
    my $reports = get_saved_reports($filter);
1137
1138
    my $branch_limit_active =
1139
           C4::Context->preference('LimitReportsByBranch')
1140
        && $can_manage_limits
1141
        && !$filter->{show_all_reports};
1142
1085
    my $has_obsolete_reports;
1143
    my $has_obsolete_reports;
1086
    for my $report (@$reports) {
1144
    for my $report (@$reports) {
1087
        $report->{results} = C4::Reports::Guided::get_results( $report->{id} );
1145
        $report->{results} = C4::Reports::Guided::get_results( $report->{id} );
Lines 1091-1103 if ( $op eq 'list' || $op eq 'convert' ) { Link Here
1091
        }
1149
        }
1092
    }
1150
    }
1093
    $template->param(
1151
    $template->param(
1094
        'manamsg'               => $input->param('manamsg') || '',
1152
        'manamsg'                => $input->param('manamsg') || '',
1095
        'saved1'                => 1,
1153
        'saved1'                 => 1,
1096
        'savedreports'          => $reports,
1154
        'savedreports'           => $reports,
1097
        'usecache'              => $usecache,
1155
        'usecache'               => $usecache,
1098
        'groups_with_subgroups' => groups_with_subgroups( $group, $subgroup ),
1156
        'groups_with_subgroups'  => groups_with_subgroups( $group, $subgroup ),
1099
        filters                 => $filter,
1157
        filters                  => $filter,
1100
        has_obsolete_reports    => $has_obsolete_reports,
1158
        has_obsolete_reports     => $has_obsolete_reports,
1159
        branch_limit_active      => $branch_limit_active,
1160
        can_manage_report_limits => $can_manage_limits,
1101
    );
1161
    );
1102
}
1162
}
1103
1163
Lines 1208-1211 sub create_non_existing_group_and_subgroup { Link Here
1208
        }
1268
        }
1209
    }
1269
    }
1210
}
1270
}
1211
(-)a/t/Koha/Auth/Permissions.t (+2 lines)
Lines 63-68 subtest 'normal staff user test' => sub { Link Here
63
        'CAN_user_reports_create_reports'                    => 1,
63
        'CAN_user_reports_create_reports'                    => 1,
64
        'CAN_user_reports_delete_reports'                    => 1,
64
        'CAN_user_reports_delete_reports'                    => 1,
65
        'CAN_user_reports_execute_reports'                   => 1,
65
        'CAN_user_reports_execute_reports'                   => 1,
66
        'CAN_user_reports_manage_report_limits'              => 1,
66
    };
67
    };
67
    is_deeply( $authz, $expected, 'Expected permissions generated for normal staff user' );
68
    is_deeply( $authz, $expected, 'Expected permissions generated for normal staff user' );
68
};
69
};
Lines 233-238 subtest 'superlibrarian tests' => sub { Link Here
233
        'CAN_user_reports_create_reports'                           => 1,
234
        'CAN_user_reports_create_reports'                           => 1,
234
        'CAN_user_reports_delete_reports'                           => 1,
235
        'CAN_user_reports_delete_reports'                           => 1,
235
        'CAN_user_reports_execute_reports'                          => 1,
236
        'CAN_user_reports_execute_reports'                          => 1,
237
        'CAN_user_reports_manage_report_limits'                     => 1,
236
        'CAN_user_reports'                                          => 1,
238
        'CAN_user_reports'                                          => 1,
237
        'CAN_user_reserveforothers_modify_holds_priority'           => 1,
239
        'CAN_user_reserveforothers_modify_holds_priority'           => 1,
238
        'CAN_user_reserveforothers_place_holds'                     => 1,
240
        'CAN_user_reserveforothers_place_holds'                     => 1,
(-)a/t/db_dependent/Koha/Reports.t (-1 / +103 lines)
Lines 18-24 Link Here
18
use Modern::Perl;
18
use Modern::Perl;
19
19
20
use Test::NoWarnings;
20
use Test::NoWarnings;
21
use Test::More tests => 9;
21
use Test::More tests => 11;
22
22
23
use Koha::Report;
23
use Koha::Report;
24
use Koha::Reports;
24
use Koha::Reports;
Lines 185-188 subtest '_might_add_limit' => sub { Link Here
185
    );
185
    );
186
};
186
};
187
187
188
subtest 'reports_branches are added and removed from report_branches table' => sub {
189
    plan tests => 4;
190
191
    my $updated_nb_of_reports = Koha::Reports->search->count;
192
    my $report                = Koha::Report->new(
193
        {
194
            report_name => 'report_name_for_test_1',
195
            savedsql    => 'SELECT * FROM items WHERE itemnumber IN <<Test|list>>',
196
        }
197
    )->store;
198
199
    my $id       = $report->id;
200
    my $library1 = $builder->build_object( { class => 'Koha::Libraries' } );
201
    my $library2 = $builder->build_object( { class => 'Koha::Libraries' } );
202
    my $library3 = $builder->build_object( { class => 'Koha::Libraries' } );
203
    my @branches = ( $library1->branchcode, $library2->branchcode, $library3->branchcode );
204
205
    $report->replace_library_limits( \@branches );
206
207
    my @branches_loop = $report->get_library_limits->as_list;
208
    is( scalar @branches_loop, 3, '3 branches added to report_branches table' );
209
210
    $report->replace_library_limits( [ $library1->branchcode, $library2->branchcode ] );
211
212
    @branches_loop = $report->get_library_limits->as_list;
213
    is( scalar @branches_loop, 2, '1 branch removed from report_branches table' );
214
215
    $report->delete;
216
    is( Koha::Reports->search->count, $updated_nb_of_reports, 'Report deleted, count is back to original' );
217
    is(
218
        $schema->resultset('ReportsBranch')->search( { report_id => $id } )->count,
219
        0,
220
        'No branches left in reports_branches table after report deletion'
221
    );
222
};
223
224
subtest 'can_manage_limits and can_access' => sub {
225
    plan tests => 21;
226
227
    my $libraryA = $builder->build_object( { class => 'Koha::Libraries' } );
228
    my $libraryB = $builder->build_object( { class => 'Koha::Libraries' } );
229
    my $branchA  = $libraryA->branchcode;
230
    my $branchB  = $libraryB->branchcode;
231
232
    my $super_patron =
233
        $builder->build_object( { class => 'Koha::Patrons', value => { flags => 1, branchcode => $branchA } } );
234
    my $mgr_patron   = $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $branchA } } );
235
    my $basic_patron = $builder->build_object( { class => 'Koha::Patrons', value => { branchcode => $branchA } } );
236
237
    # Grant reports => manage_report_limits to manager patron (create permission if missing)
238
    my $perm = $schema->resultset('Permission')->find( { module_bit => 16, code => 'manage_report_limits' } )
239
        // $schema->resultset('Permission')
240
        ->create( { module_bit => 16, code => 'manage_report_limits', description => 'Manage report limits' } );
241
    $schema->resultset('UserPermission')
242
        ->create( { borrowernumber => $mgr_patron->borrowernumber, module_bit => 16, code => 'manage_report_limits' } );
243
244
    # Helper to create reports
245
    my $r_no = Koha::Report->new( { report_name => 'No limits', savedsql => 'SELECT 1' } )->store;
246
    my $r_A  = Koha::Report->new( { report_name => 'Limit A',   savedsql => 'SELECT 1' } )->store;
247
    my $r_B  = Koha::Report->new( { report_name => 'Limit B',   savedsql => 'SELECT 1' } )->store;
248
    my $r_AB = Koha::Report->new( { report_name => 'Limit AB',  savedsql => 'SELECT 1' } )->store;
249
250
    $r_A->replace_library_limits( [$branchA] );
251
    $r_B->replace_library_limits( [$branchB] );
252
    $r_AB->replace_library_limits( [ $branchA, $branchB ] );
253
254
    # Preference ON, branch A
255
    t::lib::Mocks::mock_preference( 'LimitReportsByBranch', 1 );
256
    t::lib::Mocks::mock_userenv( { branchcode => $branchA } );
257
258
    ok( Koha::Report->can_manage_limits($super_patron),  'pref ON: super manages limits' );
259
    ok( Koha::Report->can_manage_limits($mgr_patron),    'pref ON: manager manages limits' );
260
    ok( !Koha::Report->can_manage_limits($basic_patron), 'pref ON: basic cannot manage limits' );
261
262
    # Basic patron (branch A)
263
    ok( $r_no->can_access($basic_patron), 'pref ON: no limits accessible' );
264
    ok( $r_A->can_access($basic_patron),  'pref ON: limited includes branch accessible' );
265
    ok( !$r_B->can_access($basic_patron), 'pref ON: limited excludes branch denied' );
266
    ok( $r_AB->can_access($basic_patron), 'pref ON: multi includes branch accessible' );
267
268
    # Manager bypass (branch A)
269
    ok( $r_A->can_access($mgr_patron),  'pref ON: manager sees limited A' );
270
    ok( $r_B->can_access($mgr_patron),  'pref ON: manager sees limited B' );
271
    ok( $r_AB->can_access($mgr_patron), 'pref ON: manager sees multi AB' );
272
    ok( $r_no->can_access($mgr_patron), 'pref ON: manager sees no limits' );
273
274
    # Superlibrarian bypass (branch A)
275
    ok( $r_A->can_access($super_patron),  'pref ON: super sees limited A' );
276
    ok( $r_B->can_access($super_patron),  'pref ON: super sees limited B' );
277
    ok( $r_AB->can_access($super_patron), 'pref ON: super sees multi AB' );
278
279
    # Preference OFF (everything accessible, manage disabled)
280
    t::lib::Mocks::mock_preference( 'LimitReportsByBranch', 0 );
281
    ok( !Koha::Report->can_manage_limits($super_patron), 'pref OFF: super cannot manage limits' );
282
    ok( !Koha::Report->can_manage_limits($mgr_patron),   'pref OFF: manager cannot manage limits' );
283
    ok( !Koha::Report->can_manage_limits($basic_patron), 'pref OFF: basic cannot manage limits' );
284
    ok( $r_B->can_access($basic_patron),                 'pref OFF: limited excludes branch still accessible' );
285
    ok( $r_A->can_access($basic_patron),                 'pref OFF: limited includes branch accessible' );
286
    ok( $r_AB->can_access($basic_patron),                'pref OFF: multi-limit accessible' );
287
    ok( $r_no->can_access($basic_patron),                'pref OFF: no limits accessible' );
288
};
289
188
$schema->storage->txn_rollback;
290
$schema->storage->txn_rollback;
(-)a/t/db_dependent/Reports/Guided.t (-1 / +86 lines)
Lines 21-27 Link Here
21
use Modern::Perl;
21
use Modern::Perl;
22
22
23
use Test::NoWarnings;
23
use Test::NoWarnings;
24
use Test::More tests => 14;
24
use Test::More tests => 15;
25
use Test::Warn;
25
use Test::Warn;
26
26
27
use t::lib::TestBuilder;
27
use t::lib::TestBuilder;
Lines 324-329 subtest 'get_saved_reports' => sub { Link Here
324
    );
324
    );
325
};
325
};
326
326
327
subtest 'get_saved_reports branch limits' => sub {
328
    my $schema = Koha::Database->new->schema;
329
    my $dbh    = C4::Context->dbh;
330
331
    # Skip gracefully if the feature table is not present (e.g. older schema)
332
    my $sth = $dbh->prepare(q{SHOW TABLES LIKE 'reports_library_limits'});
333
    $sth->execute;
334
    my ($exists) = $sth->fetchrow_array;
335
    unless ($exists) {
336
        plan skip_all => 'reports_library_limits table not present; skipping branch limits tests';
337
        return;
338
    }
339
340
    plan tests => 13;
341
342
    # Clean related tables
343
    $dbh->do(q|DELETE FROM saved_sql|);
344
    $dbh->do(q|DELETE FROM saved_reports|);
345
    $dbh->do(q|DELETE FROM reports_library_limits|);
346
347
    # Build two libraries (branches)
348
    my $lib_a = $builder->build( { source => 'Branch', value => 'Library A' } );
349
    my $lib_b = $builder->build( { source => 'Branch', value => 'Library B' } );
350
351
    # Mock userenv to lib_a
352
    t::lib::Mocks::mock_userenv( { branchcode => $lib_a->{branchcode} } );
353
354
    # Build three reports
355
    my @ids;
356
    foreach my $n ( 1 .. 3 ) {
357
        push @ids, save_report(
358
            {
359
                borrowernumber => $builder->build( { source => 'Borrower' } )->{borrowernumber},
360
                sql            => "SELECT $n",
361
                name           => "Report $n",
362
                area           => 'CIRC',
363
                group          => 'G',
364
                subgroup       => 'SG',
365
                type           => 'T',
366
                notes          => undef,
367
                cache_expiry   => undef,
368
                public         => 0,
369
            }
370
        );
371
    }
372
373
    my ( $r1, $r2, $r3 ) = map { Koha::Reports->find($_) } @ids;
374
375
    # Apply library limits to first two reports (r1 => lib_a only, r2 => lib_b only)
376
    $r1->replace_library_limits( [ $lib_a->{branchcode} ] );
377
    $r2->replace_library_limits( [ $lib_b->{branchcode} ] );
378
379
    # r3 has no limits
380
381
    # Preference OFF: should ignore limits, return all three, no library_limits metadata
382
    t::lib::Mocks::mock_preference( 'LimitReportsByBranch', 0 );
383
    my $all_pref_off = get_saved_reports();
384
    is( scalar(@$all_pref_off), 3, 'Pref OFF returns all reports' );
385
    ok( !defined $all_pref_off->[0]{library_limits}, 'Pref OFF does not attach metadata' );
386
387
    # Preference ON, filtering applied (userenv branch is lib_a)
388
    t::lib::Mocks::mock_preference( 'LimitReportsByBranch', 1 );
389
    my $filtered = get_saved_reports();
390
    is( scalar(@$filtered), 2, 'Pref ON filters reports by user branch and unrestricted reports' );
391
    my @filtered_ids = sort map { $_->{id} } @$filtered;
392
    my @expected_ids = sort ( $r1->id, $r3->id );          # r1 limited to lib_a + r3 unrestricted
393
    is_deeply( \@filtered_ids, \@expected_ids, 'Filtered list contains branch-limited and unrestricted reports' );
394
    foreach my $r (@$filtered) {
395
        ok( defined $r->{library_limits}, 'Metadata attached on pref ON filtered path' );
396
    }
397
    my ($meta_r1) = grep { $_->{id} == $r1->id } @$filtered;
398
    is_deeply( $meta_r1->{library_limits}, [ $lib_a->{branchcode} ], 'r1 metadata lists its single limit' );
399
    my ($meta_r3) = grep { $_->{id} == $r3->id } @$filtered;
400
    is_deeply( $meta_r3->{library_limits}, [], 'Unrestricted r3 has empty limits array' );
401
402
    # Preference ON + show_all_reports flag: should return all, with metadata for all
403
    my $show_all = get_saved_reports( { show_all_reports => 1 } );
404
    is( scalar(@$show_all), 3, 'show_all_reports returns all reports when pref ON' );
405
    foreach my $r (@$show_all) {
406
        ok( defined $r->{library_limits}, 'Metadata attached for each report in show_all path' );
407
    }
408
    my ($show_r2) = grep { $_->{id} == $r2->id } @$show_all;
409
    is_deeply( $show_r2->{library_limits}, [ $lib_b->{branchcode} ], 'r2 metadata lists its limit under show_all' );
410
};
411
327
subtest 'Ensure last_run is populated' => sub {
412
subtest 'Ensure last_run is populated' => sub {
328
    plan tests => 3;
413
    plan tests => 3;
329
414
(-)a/tools/scheduler.pl (-6 / +36 lines)
Lines 27-32 use C4::Output qw( output_html_with_http_headers ); Link Here
27
use Koha::DateUtils     qw( dt_from_string );
27
use Koha::DateUtils     qw( dt_from_string );
28
use Koha::Reports;
28
use Koha::Reports;
29
use Koha::Email;
29
use Koha::Email;
30
use Koha::Patrons;
31
use Koha::Report;
30
32
31
my $input = CGI->new;
33
my $input = CGI->new;
32
my $base;
34
my $base;
Lines 48-55 my ( $template, $borrowernumber, $cookie ) = get_template_and_user( Link Here
48
    }
50
    }
49
);
51
);
50
52
51
my $op = $input->param('op') // q{};
53
my $op                 = $input->param('op') // q{};
52
my $id = $input->param('id');
54
my $id                 = $input->param('id');
55
my $selected_report_id = $input->param('report');
56
my $selected_report    = $selected_report_id ? Koha::Reports->find($selected_report_id) : undef;
57
if ( $op eq 'cud-add' && $selected_report_id ) {
58
    my $logged_in_user = Koha::Patrons->find($borrowernumber);
59
    if ( !$selected_report ) {
60
        $template->param( no_sql_for_id => $selected_report_id, access_denied => 1 );
61
        $op = '';
62
    } elsif ( !$selected_report->can_access($logged_in_user) ) {
63
        $template->param( access_denied => 1, id => $selected_report_id );
64
        $op = '';
65
    }
66
}
53
67
54
if ( $op eq 'cud-add' ) {
68
if ( $op eq 'cud-add' ) {
55
69
Lines 111-124 foreach my $job ( values %$jobs ) { Link Here
111
125
112
@jobloop = sort { $a->{TIME} cmp $b->{TIME} } @jobloop;
126
@jobloop = sort { $a->{TIME} cmp $b->{TIME} } @jobloop;
113
127
114
my $reports = get_saved_reports();
128
my $logged_in_user    = Koha::Patrons->find($borrowernumber);
129
my $can_manage_limits = Koha::Report->can_manage_limits($logged_in_user);
130
131
my $reports =
132
    $can_manage_limits
133
    ? get_saved_reports( { show_all_reports => 1 } )
134
    : get_saved_reports();
135
136
my @final_reports;
137
if ($can_manage_limits) {
138
    @final_reports = @$reports;
139
} else {
140
    @final_reports = grep {
141
        my $r_obj = Koha::Reports->find( $_->{id} );
142
        $r_obj && $r_obj->can_access($logged_in_user);
143
    } @$reports;
144
}
145
115
if ( defined $id ) {
146
if ( defined $id ) {
116
    foreach my $report (@$reports) {
147
    foreach my $report (@final_reports) {
117
        $report->{'selected'} = 1 if $report->{'id'} eq $id;
148
        $report->{'selected'} = 1 if $report->{'id'} eq $id;
118
    }
149
    }
119
}
150
}
120
151
121
$template->param( 'savedreports' => $reports );
152
$template->param( 'savedreports' => \@final_reports );
122
$template->param( JOBS           => \@jobloop );
153
$template->param( JOBS           => \@jobloop );
123
my $time = localtime(time);
154
my $time = localtime(time);
124
$template->param( 'time' => $time );
155
$template->param( 'time' => $time );
125
- 

Return to bug 16631