diff --git a/Koha/AdditionalContent.pm b/Koha/AdditionalContent.pm index 3b2807a8496..c81ba995ebc 100644 --- a/Koha/AdditionalContent.pm +++ b/Koha/AdditionalContent.pm @@ -142,6 +142,43 @@ sub translated_content { return $content; } +=head3 public_read_list + +This method returns the list of publicly readable database fields for both API and UI output purposes + +=cut + +sub public_read_list { + return [ + 'id', 'category', 'code', + 'location', 'branchcode', 'published_on', + 'updated_on', 'expirationdate', 'number', + 'borrowernumber' + ]; +} + +=head3 to_api_mapping + +This method returns the mapping for representing a Koha::AdditionalContent object +on the API. + +=cut + +sub to_api_mapping { + return { + id => 'additional_content_id', + category => 'category', + code => 'code', + location => 'location', + branchcode => 'library_id', + published_on => 'published_on', + updated_on => 'updated_on', + expirationdate => 'expirationdate', + number => 'number', + borrowernumber => 'patron_id', + }; +} + =head3 _type =cut diff --git a/Koha/AdditionalContents.pm b/Koha/AdditionalContents.pm index a16089e5247..10f37a4dbad 100644 --- a/Koha/AdditionalContents.pm +++ b/Koha/AdditionalContents.pm @@ -38,6 +38,26 @@ Koha::AdditionalContents - Koha Additional content object set class =cut +=head3 get_public_query_search_params + + my $public_query_search_params = $self->get_public_query_search_params($params); + +=cut + +sub get_public_query_search_params { + my ($params) = @_; + + my $search_params; + $search_params->{'additional_content.id'} = $params->{id} if $params->{id}; + $search_params->{location} = $params->{location}; + $search_params->{branchcode} = $params->{library_id} ? [ $params->{library_id}, undef ] : undef; + $search_params->{published_on} = { '<=' => \'CAST(NOW() AS DATE)' } unless $params->{id}; + $search_params->{expirationdate} = [ '-or', { '>=' => \'CAST(NOW() AS DATE)' }, undef ] unless $params->{id}; + $search_params->{category} = $params->{category} if $params->{category}; + + return $search_params; +} + =head3 search_for_display my $contents = Koha::AdditionalContents->search_for_display({ @@ -80,15 +100,9 @@ sub search_for_display { my $subquery = qq|(SELECT COUNT(*) FROM additional_contents_localizations WHERE lang='$lang' AND additional_content_id=me.additional_content_id)=0|; - my $search_params; - $search_params->{'additional_content.id'} = $params->{id} if $params->{id}; - $search_params->{location} = $params->{location}; - $search_params->{branchcode} = $params->{library_id} ? [ $params->{library_id}, undef ] : undef; - $search_params->{published_on} = { '<=' => \'CAST(NOW() AS DATE)' } unless $params->{id}; - $search_params->{expirationdate} = [ '-or', { '>=' => \'CAST(NOW() AS DATE)' }, undef ] unless $params->{id}; - $search_params->{category} = $params->{category} if $params->{category}; - $search_params->{lang} = 'default' if !$lang || $lang eq 'default'; - $search_params->{-or} = [ { 'lang' => $lang }, '-and' => [ 'lang', 'default', \$subquery ] ] + my $search_params = get_public_query_search_params($params); + $search_params->{lang} = 'default' if !$lang || $lang eq 'default'; + $search_params->{-or} = [ { 'lang' => $lang }, '-and' => [ 'lang', 'default', \$subquery ] ] if !$search_params->{lang}; my $attribs = { prefetch => 'additional_content', order_by => 'additional_content.number' }; @@ -139,6 +153,39 @@ sub find_best_match { return $alt1 // $alt2 // $alt3; } +=head3 get_html_customizations_options + + Koha::AdditionalContents->get_html_customizations_options('opac'); + +=cut + +sub get_html_customizations_options { + my ($interface) = @_; + + if ( $interface eq 'opac' ) { + return [ + 'OpacNavRight', 'opacheader', 'OpacCustomSearch', 'OpacMainUserBlock', 'opaccredits', + 'OpacLoginInstructions', 'OpacNav', 'OpacNavBottom', 'OpacSuggestionInstructions', + 'ArticleRequestsDisclaimerText', 'OpacMoreSearches', 'OpacMySummaryNote', 'OpacLibraryInfo', + 'OpacMaintenanceNotice', 'OPACResultsSidebar', 'OpacSuppressionMessage', 'SCOMainUserBlock', + 'SelfCheckInMainUserBlock', 'SelfCheckHelpMessage', 'CatalogConcernHelp', 'CatalogConcernTemplate', + 'CookieConsentBar', 'CookieConsentPopup', 'PatronSelfRegistrationAdditionalInstructions', + 'ILLModuleCopyrightClearance' + ]; + } + + if ( $interface eq 'staff' ) { + return [ + 'IntranetmainUserblock', 'RoutingListNote', 'StaffAcquisitionsHome', 'StaffAuthoritiesHome', + 'StaffCataloguingHome', 'StaffListsHome', 'StaffLoginInstructions', 'StaffPatronsHome', 'StaffPOSHome', + 'StaffSerialsHome' + ]; + } + + return []; + +} + =head3 _type =cut diff --git a/Koha/AdditionalContentsLocalization.pm b/Koha/AdditionalContentsLocalization.pm index 20302f76cab..104fd051431 100644 --- a/Koha/AdditionalContentsLocalization.pm +++ b/Koha/AdditionalContentsLocalization.pm @@ -189,6 +189,19 @@ sub borrowernumber { return $self->additional_content->borrowernumber(@params); } +=head3 public_read_list + +This method returns the list of publicly readable database fields for both API and UI output purposes + +=cut + +sub public_read_list { + return [ + 'id', 'additional_content_id', 'title', + 'content', 'lang', 'updated_on', + ]; +} + =head2 Class Methods =cut diff --git a/Koha/REST/V1/AdditionalContents.pm b/Koha/REST/V1/AdditionalContents.pm new file mode 100644 index 00000000000..94736519887 --- /dev/null +++ b/Koha/REST/V1/AdditionalContents.pm @@ -0,0 +1,60 @@ +package Koha::REST::V1::AdditionalContents; + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use Mojo::Base 'Mojolicious::Controller'; + +use Koha::AdditionalContents; + +use Try::Tiny qw( catch try ); + +=head1 API + +=head2 Methods + +=head3 list_public + +Controller function that handles retrieving a list of additional contents + +=cut + +sub list_public { + my $c = shift->openapi->valid_input or return; + + return try { + + my @public_locations = ( + @{ Koha::AdditionalContents::get_html_customizations_options('opac') }, + 'staff_and_opac', + 'opac_only' + ); + + my $public_additional_contents_query = + Koha::AdditionalContents::get_public_query_search_params( { location => { '-in' => \@public_locations } } ); + + my $additional_contents = + $c->objects->search( Koha::AdditionalContents->search($public_additional_contents_query) ); + + return $c->render( status => 200, openapi => $additional_contents ); + } catch { + $c->unhandled_exception($_); + }; + +} + +1; diff --git a/api/v1/swagger/definitions/additional_content.yaml b/api/v1/swagger/definitions/additional_content.yaml new file mode 100644 index 00000000000..9de4580692c --- /dev/null +++ b/api/v1/swagger/definitions/additional_content.yaml @@ -0,0 +1,48 @@ +--- +additionalProperties: false +properties: + additional_content_id: + description: Internal identifier for the additional content + type: integer + category: + description: Category of the additional content + type: string + code: + description: Code of the additional content + type: string + location: + description: Location of the additional content + type: string + library_id: + description: Library id of the additional content + type: + - string + - "null" + published_on: + description: Publication date of the additional content + type: string + updated_on: + description: Last modification date of the additional content + type: string + expirationdate: + description: Expiration date of the additional content + type: string + number: + description: The order in which this additional content appears in that specific location + type: integer + patron_id: + description: The author of the additional content + type: + - string + - "null" + translated_contents: + description: Related additional contents translations + type: + - array + - "null" +required: + - additional_content_id + - category + - code + - location +type: object diff --git a/api/v1/swagger/paths/additional_contents.yaml b/api/v1/swagger/paths/additional_contents.yaml new file mode 100644 index 00000000000..b5a94a3adc7 --- /dev/null +++ b/api/v1/swagger/paths/additional_contents.yaml @@ -0,0 +1,78 @@ +--- +"/public/additional_contents": + get: + x-mojo-to: AdditionalContents#list_public + operationId: listAdditionalContents + parameters: + - description: Case insensitive search on additional_contents id + in: query + name: additional_content_id + required: false + type: string + - description: Case insensitive search on additional_contents category + in: query + name: category + required: false + type: string + - description: Case insensitive search on additional_contents code + in: query + name: code + required: false + type: string + - description: Case insensitive search on additional_contents location + in: query + name: location + required: false + type: string + - description: Case insensitive search on additional_contents library_id + in: query + name: library_id + required: false + type: string + - $ref: "../swagger.yaml#/parameters/match" + - $ref: "../swagger.yaml#/parameters/order_by" + - $ref: "../swagger.yaml#/parameters/page" + - $ref: "../swagger.yaml#/parameters/per_page" + - $ref: "../swagger.yaml#/parameters/q_param" + - $ref: "../swagger.yaml#/parameters/q_body" + - name: x-koha-embed + in: header + required: false + description: Embed list sent as a request header + type: array + items: + type: string + enum: + - translated_contents + collectionFormat: csv + produces: + - application/json + responses: + 200: + description: A list of additional contents + schema: + items: + $ref: ../swagger.yaml#/definitions/additional_content + type: array + 400: + description: | + Bad request. Possible `error_code` attribute values: + + * `invalid_query` + schema: + $ref: "../swagger.yaml#/definitions/error" + 403: + description: Access forbidden + schema: + $ref: ../swagger.yaml#/definitions/error + 500: + description: Internal error + schema: + $ref: ../swagger.yaml#/definitions/error + 503: + description: Under maintenance + schema: + $ref: ../swagger.yaml#/definitions/error + summary: List public additional contents + tags: + - additional_contents \ No newline at end of file diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index d38e04e8201..0ef973f0fbb 100644 --- a/api/v1/swagger/swagger.yaml +++ b/api/v1/swagger/swagger.yaml @@ -4,6 +4,8 @@ basePath: /api/v1 definitions: account_line: $ref: ./definitions/account_line.yaml + additional_content: + $ref: ./definitions/additional_content.yaml advancededitormacro: $ref: ./definitions/advancededitormacro.yaml allows_renewal: @@ -507,6 +509,8 @@ paths: $ref: ./paths/preservation_waiting_list.yaml#/~1preservation~1waiting-list~1items "/preservation/waiting-list/items/{item_id}": $ref: "./paths/preservation_waiting_list.yaml#/~1preservation~1waiting-list~1items~1{item_id}" + "/public/additional_contents": + $ref: ./paths/additional_contents.yaml#/~1public~1additional_contents "/public/biblios/{biblio_id}": $ref: "./paths/biblios.yaml#/~1public~1biblios~1{biblio_id}" "/public/checkouts/availability": diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/additional-contents.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/additional-contents.tt index 330b5522acb..a5884db4bb6 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/additional-contents.tt +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/additional-contents.tt @@ -532,7 +532,6 @@ [% END %] [% END %] [% ELSE %] - [% SET opac_available_options = [ 'OpacNavRight', 'opacheader', 'OpacCustomSearch', 'OpacMainUserBlock', 'opaccredits', 'OpacLoginInstructions', 'OpacNav', 'OpacNavBottom', 'OpacSuggestionInstructions', 'ArticleRequestsDisclaimerText', 'OpacMoreSearches', 'OpacMySummaryNote', 'OpacLibraryInfo', 'OpacMaintenanceNotice', 'OPACResultsSidebar', 'OpacSuppressionMessage', 'SCOMainUserBlock', 'SelfCheckInMainUserBlock', 'SelfCheckHelpMessage', 'CatalogConcernHelp', 'CatalogConcernTemplate', 'CookieConsentBar', 'CookieConsentPopup', 'PatronSelfRegistrationAdditionalInstructions', 'ILLModuleCopyrightClearance' ] %] [% FOREACH l IN opac_available_options.sort %] [% IF l == location %] @@ -542,7 +541,6 @@ [% END %] [% END %] - [% SET staff_available_options = [ 'IntranetmainUserblock', 'RoutingListNote', 'StaffAcquisitionsHome', 'StaffAuthoritiesHome', 'StaffCataloguingHome', 'StaffListsHome', 'StaffLoginInstructions', 'StaffPatronsHome', 'StaffPOSHome', 'StaffSerialsHome' ] %] [% FOREACH l IN staff_available_options.sort %] [% IF l == location %] diff --git a/t/db_dependent/api/v1/additional_contents.t b/t/db_dependent/api/v1/additional_contents.t new file mode 100755 index 00000000000..3d1b4ef3697 --- /dev/null +++ b/t/db_dependent/api/v1/additional_contents.t @@ -0,0 +1,97 @@ +#!/usr/bin/env perl + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +use Modern::Perl; + +use Test::NoWarnings; +use Test::More tests => 2; +use Test::Mojo; + +use t::lib::TestBuilder; +use t::lib::Mocks; + +use Koha::AdditionalContents; +use Koha::Database; +use Koha::DateUtils qw( dt_from_string ); + +my $schema = Koha::Database->new->schema; +my $builder = t::lib::TestBuilder->new; + +my $t = Test::Mojo->new('Koha::REST::V1'); +t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); + +subtest 'anonymous access' => sub { + plan tests => 9; + + $schema->storage->txn_begin; + + Koha::AdditionalContents->search->delete; + + my $today = dt_from_string; + my $yesterday = dt_from_string->add( days => -1 ); + my $tomorrow = dt_from_string->add( days => 1 ); + my $res; + + $builder->build_object( + { + class => 'Koha::AdditionalContents', + value => { + expirationdate => $tomorrow, + published_on => $tomorrow, + category => 'news', + location => 'staff_and_opac', + branchcode => undef, + number => 3, + } + } + ); + + t::lib::Mocks::mock_preference( 'RESTPublicAnonymousRequests', 1 ); + + $res = $t->get_ok("/api/v1/public/additional_contents")->status_is(200)->tx->res->json; + + is( scalar @{$res}, 0, 'The only additional content is not active and not public' ); + + my $public_additional_contents = $builder->build_object( + { + class => 'Koha::AdditionalContents', + value => { + expirationdate => $tomorrow, + published_on => $yesterday, + category => 'news', + location => 'staff_and_opac', + branchcode => undef, + number => 3, + } + } + ); + + $res = $t->get_ok("/api/v1/public/additional_contents")->status_is(200)->tx->res->json; + + is( scalar @{$res}, 1, 'There is now one active and public additional content' ); + + $t->get_ok( "/api/v1/public/additional_contents" => { 'x-koha-embed' => 'translated_contents' } )->status_is(200) + ->json_is( + '/0' => { + %{ $public_additional_contents->to_api( { public => 1 } ) }, + translated_contents => $public_additional_contents->translated_contents->to_api( { public => 1 } ) + } + ); + + $schema->storage->txn_rollback; + +}; diff --git a/tools/additional-contents.pl b/tools/additional-contents.pl index 89dacf9589c..6809a944643 100755 --- a/tools/additional-contents.pl +++ b/tools/additional-contents.pl @@ -72,8 +72,10 @@ $category = $additional_content->category; } $template->param( - additional_content => $additional_content, - translated_contents => $translated_contents, + additional_content => $additional_content, + translated_contents => $translated_contents, + opac_available_options => Koha::AdditionalContents::get_html_customizations_options('opac'), + staff_available_options => Koha::AdditionalContents::get_html_customizations_options('staff'), ); } elsif ( $op eq 'cud-add_validate' ) { output_and_exit_if_error( $cgi, $cookie, $template, { check => 'csrf_token' } );