--- /dev/null
+package Koha::AdvancedEditorMacro;
+
+# 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, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use Modern::Perl;
+
+use Carp;
+
+use Koha::Database;
+
+use base qw(Koha::Object);
+
+=head1 NAME
+
+Koha::AdvancedEditorMacro - Koha Advanced Editor Macro Object class
+
+=head1 API
+
+=head2 Class methods
+
+=head3 to_api_mapping
+
+=cut
+
+sub to_api_mapping {
+ return {
+ id => 'macro_id',
+ macro => 'macro_text',
+ borrowernumber => 'patron_id',
+ };
+}
+
+
+=head2 Internal methods
+
+=head3 _type
+
+=cut
+
+sub _type {
+ return 'AdvancedEditorMacro';
+}
+
+1;
--- /dev/null
+package Koha::AdvancedEditorMacros;
+
+# 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, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use Modern::Perl;
+
+use Carp;
+
+use Koha::Database;
+
+use Koha::AdvancedEditorMacro;
+
+use base qw(Koha::Objects);
+
+=head1 NAME
+
+Koha::AdvancedEditorMacros - Koha Advanced Editor Macro Object set class
+
+=head1 API
+
+=head2 Class Methods
+
+=cut
+
+=head3 type
+
+=cut
+
+sub _type {
+ return 'AdvancedEditorMacro';
+}
+
+sub object_class {
+ return 'Koha::AdvancedEditorMacro';
+}
+
+1;
--- /dev/null
+package Koha::REST::V1::AdvancedEditorMacro;
+
+# 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, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use Modern::Perl;
+
+use Mojo::Base 'Mojolicious::Controller';
+use Koha::AdvancedEditorMacros;
+
+use Try::Tiny;
+
+=head1 API
+
+=head2 Class Methods
+
+=cut
+
+=head3 list
+
+Controller function that handles listing Koha::AdvancedEditorMacro objects
+
+=cut
+
+sub list {
+ my $c = shift->openapi->valid_input or return;
+ my $patron = $c->stash('koha.user');
+ return try {
+ my $macros_set = Koha::AdvancedEditorMacros->search({ -or => { shared => 1, borrowernumber => $patron->borrowernumber } });
+ my $macros = $c->objects->search( $macros_set, \&_to_model, \&_to_api );
+ return $c->render( status => 200, openapi => $macros );
+ }
+ catch {
+ if ( $_->isa('DBIx::Class::Exception') ) {
+ return $c->render( status => 500,
+ openapi => { error => $_->{msg} } );
+ }
+ else {
+ return $c->render( status => 500,
+ openapi => { error => "Something went wrong, check the logs. $_"} );
+ }
+ };
+
+}
+
+=head3 get
+
+Controller function that handles retrieving a single Koha::AdvancedEditorMacro
+
+=cut
+
+sub get {
+ my $c = shift->openapi->valid_input or return;
+ my $patron = $c->stash('koha.user');
+ my $macro = Koha::AdvancedEditorMacros->find({
+ id => $c->validation->param('advancededitormacro_id'),
+ });
+ unless ($macro) {
+ return $c->render( status => 404,
+ openapi => { error => "Macro not found" } );
+ }
+ if( $macro->shared ){
+ return $c->render( status => 403, openapi => {
+ error => "This macro is shared, you must access it via advancededitormacros/shared"
+ });
+ }
+ warn $macro->borrowernumber;
+ warn $patron->borrowernumber;
+ if( $macro->borrowernumber != $patron->borrowernumber ){
+ return $c->render( status => 403, openapi => {
+ error => "You do not have permission to access this macro"
+ });
+ }
+
+ return $c->render( status => 200, openapi => $macro->to_api );
+}
+
+=head3 get_shared
+
+Controller function that handles retrieving a single Koha::AdvancedEditorMacro
+
+=cut
+
+sub get_shared {
+ my $c = shift->openapi->valid_input or return;
+ my $patron = $c->stash('koha.user');
+ my $macro = Koha::AdvancedEditorMacros->find({
+ id => $c->validation->param('advancededitormacro_id'),
+ });
+ unless ($macro) {
+ return $c->render( status => 404,
+ openapi => { error => "Macro not found" } );
+ }
+ unless( $macro->shared ){
+ return $c->render( status => 403, openapi => {
+ error => "This macro is not shared, you must access it via advancededitormacros"
+ });
+ }
+ return $c->render( status => 200, openapi => $macro->to_api );
+}
+
+=head3 add
+
+Controller function that handles adding a new Koha::AdvancedEditorMacro object
+
+=cut
+
+sub add {
+ my $c = shift->openapi->valid_input or return;
+
+ if( defined $c->validation->param('body')->{shared} && $c->validation->param('body')->{shared} == 1 ){
+ return $c->render( status => 403,
+ openapi => { error => "To create shared macros you must use advancededitor/shared" } );
+ }
+
+ return try {
+ my $macro = Koha::AdvancedEditorMacro->new( _to_model( $c->validation->param('body') ) );
+ $macro->store;
+ $c->res->headers->location( $c->req->url->to_string . '/' . $macro->id );
+ return $c->render(
+ status => 201,
+ openapi => $macro->to_api
+ );
+ }
+ catch { handle_error($_) };
+}
+
+=head3 add_shared
+
+Controller function that handles adding a new shared Koha::AdvancedEditorMacro object
+
+=cut
+
+sub add_shared {
+ my $c = shift->openapi->valid_input or return;
+
+ unless( defined $c->validation->param('body')->{shared} && $c->validation->param('body')->{shared} == 1 ){
+ return $c->render( status => 403,
+ openapi => { error => "To create private macros you must use advancededitor" } );
+ }
+
+ return try {
+ my $macro = Koha::AdvancedEditorMacro->new( _to_model( $c->validation->param('body') ) );
+ $macro->store;
+ $c->res->headers->location( $c->req->url->to_string . '/' . $macro->id );
+ return $c->render(
+ status => 201,
+ openapi => $macro->to_api
+ );
+ }
+ catch { handle_error($_) };
+}
+
+=head3 update
+
+Controller function that handles updating a Koha::AdvancedEditorMacro object
+
+=cut
+
+sub update {
+ my $c = shift->openapi->valid_input or return;
+
+ my $macro = Koha::AdvancedEditorMacros->find( $c->validation->param('advancededitormacro_id') );
+
+ if ( not defined $macro ) {
+ return $c->render( status => 404,
+ openapi => { error => "Object not found" } );
+ }
+ my $patron = $c->stash('koha.user');
+
+ if( $macro->shared == 1 || defined $c->validation->param('body')->{shared} && $c->validation->param('body')->{shared} == 1 ){
+ return $c->render( status => 403,
+ openapi => { error => "To update a macro as shared you must use the advancededitormacros/shared endpoint" } );
+ } else {
+ unless ( $macro->borrowernumber == $patron->borrowernumber ){
+ return $c->render( status => 403,
+ openapi => { error => "You can only edit macros you own" } );
+ }
+ }
+
+ return try {
+ my $params = $c->req->json;
+ $macro->set( _to_model($params) );
+ $macro->store();
+ return $c->render( status => 200, openapi => $macro->to_api );
+ }
+ catch { handle_error($_) };
+}
+
+=head3 update_shared
+
+Controller function that handles updating a shared Koha::AdvancedEditorMacro object
+
+=cut
+
+sub update_shared {
+ my $c = shift->openapi->valid_input or return;
+
+ my $macro = Koha::AdvancedEditorMacros->find( $c->validation->param('advancededitormacro_id') );
+
+ if ( not defined $macro ) {
+ return $c->render( status => 404,
+ openapi => { error => "Object not found" } );
+ }
+
+ unless( $macro->shared == 1 || defined $c->validation->param('body')->{shared} && $c->validation->param('body')->{shared} == 1 ){
+ return $c->render( status => 403,
+ openapi => { error => "You can only update shared macros using this endpoint" } );
+ }
+
+ return try {
+ my $params = $c->req->json;
+ $macro->set( _to_model($params) );
+ $macro->store();
+ return $c->render( status => 200, openapi => $macro->to_api );
+ }
+ catch { handle_error($_) };
+}
+
+=head3 delete
+
+Controller function that handles deleting a Koha::AdvancedEditorMacro object
+
+=cut
+
+sub delete {
+ my $c = shift->openapi->valid_input or return;
+
+ my $macro = Koha::AdvancedEditorMacros->find( $c->validation->param('advancededitormacro_id') );
+ if ( not defined $macro ) {
+ return $c->render( status => 404,
+ openapi => { error => "Object not found" } );
+ }
+
+ my $patron = $c->stash('koha.user');
+ if( $macro->shared == 1 ){
+ return $c->render( status => 403,
+ openapi => { error => "You cannot delete shared macros using this endpoint" } );
+ } else {
+ unless ( $macro->borrowernumber == $patron->borrowernumber ){
+ return $c->render( status => 403,
+ openapi => { error => "You can only delete macros you own" } );
+ }
+ }
+
+ return try {
+ $macro->delete;
+ return $c->render( status => 200, openapi => "" );
+ }
+ catch { handle_error($_) };
+}
+
+=head3 delete_shared
+
+Controller function that handles deleting a shared Koha::AdvancedEditorMacro object
+
+=cut
+
+sub delete_shared {
+ my $c = shift->openapi->valid_input or return;
+
+ my $macro = Koha::AdvancedEditorMacros->find( $c->validation->param('advancededitormacro_id') );
+ if ( not defined $macro ) {
+ return $c->render( status => 404,
+ openapi => { error => "Object not found" } );
+ }
+
+ unless( $macro->shared == 1 ){
+ return $c->render( status => 403,
+ openapi => { error => "You can only delete shared macros using this endpoint" } );
+ }
+
+ return try {
+ $macro->delete;
+ return $c->render( status => 200, openapi => "" );
+ }
+ catch { handle_error($_,$c) };
+}
+
+=head3 _handle_error
+
+Helper function that passes exception or error
+
+=cut
+
+sub _handle_error {
+ my ($err,$c) = @_;
+ if ( $err->isa('DBIx::Class::Exception') ) {
+ return $c->render( status => 500,
+ openapi => { error => $err->{msg} } );
+ }
+ else {
+ return $c->render( status => 500,
+ openapi => { error => "Something went wrong, check the logs."} );
+ }
+};
+
+
+=head3 _to_api
+
+Helper function that maps a hashref of Koha::AdvancedEditorMacro attributes into REST api
+attribute names.
+
+=cut
+
+sub _to_api {
+ my $macro = shift;
+
+ # Rename attributes
+ foreach my $column ( keys %{ $Koha::REST::V1::AdvancedEditorMacro::to_api_mapping } ) {
+ my $mapped_column = $Koha::REST::V1::AdvancedEditorMacro::to_api_mapping->{$column};
+ if ( exists $macro->{ $column }
+ && defined $mapped_column )
+ {
+ # key /= undef
+ $macro->{ $mapped_column } = delete $macro->{ $column };
+ }
+ elsif ( exists $macro->{ $column }
+ && !defined $mapped_column )
+ {
+ # key == undef => to be deleted
+ delete $macro->{ $column };
+ }
+ }
+
+ return $macro;
+}
+
+=head3 _to_model
+
+Helper function that maps REST api objects into Koha::AdvancedEditorMacros
+attribute names.
+
+=cut
+
+sub _to_model {
+ my $macro = shift;
+
+ foreach my $attribute ( keys %{ $Koha::REST::V1::AdvancedEditorMacro::to_model_mapping } ) {
+ my $mapped_attribute = $Koha::REST::V1::AdvancedEditorMacro::to_model_mapping->{$attribute};
+ if ( exists $macro->{ $attribute }
+ && defined $mapped_attribute )
+ {
+ # key /= undef
+ $macro->{ $mapped_attribute } = delete $macro->{ $attribute };
+ }
+ elsif ( exists $macro->{ $attribute }
+ && !defined $mapped_attribute )
+ {
+ # key == undef => to be deleted
+ delete $macro->{ $attribute };
+ }
+ }
+
+ if ( exists $macro->{shared} ) {
+ $macro->{shared} = ($macro->{shared}) ? 1 : 0;
+ }
+
+
+ return $macro;
+}
+
+=head2 Global variables
+
+=head3 $to_api_mapping
+
+=cut
+
+our $to_api_mapping = {
+ id => 'macro_id',
+ macro => 'macro_text',
+ borrowernumber => 'patron_id',
+};
+
+=head3 $to_model_mapping
+
+=cut
+
+our $to_model_mapping = {
+ macro_id => 'id',
+ macro_text => 'macro',
+ patron_id => 'borrowernumber',
+};
+
+1;
"order": {
"$ref": "definitions/order.json"
},
+ "advancededitormacro": {
+ "$ref": "definitions/advancededitormacro.json"
+ },
"patron": {
"$ref": "definitions/patron.json"
},
--- /dev/null
+{
+ "type": "object",
+ "properties": {
+ "macro_id": {
+ "$ref": "../x-primitives.json#/advancededitormacro_id"
+ },
+ "name": {
+ "description": "macro name",
+ "type": "string"
+ },
+ "macro_text": {
+ "description": "macro text",
+ "type": ["string", "null"]
+ },
+ "patron_id": {
+ "description": "borrower number",
+ "type": ["integer", "null"]
+ },
+ "shared": {
+ "description": "is macro shared",
+ "type": ["boolean", "null"]
+ }
+ },
+ "additionalProperties": false,
+ "required": ["name", "macro_text", "patron_id", "shared"]
+}
"biblio_id_pp": {
"$ref": "parameters/biblio.json#/biblio_id_pp"
},
+ "advancededitormacro_id_pp": {
+ "$ref": "parameters/advancededitormacro.json#/advancededitormacro_id_pp"
+ },
"patron_id_pp": {
"$ref": "parameters/patron.json#/patron_id_pp"
},
--- /dev/null
+{
+ "advancededitormacro_id_pp": {
+ "name": "advancededitormacro_id",
+ "in": "path",
+ "description": "Advanced Editor Macro internal identifier",
+ "required": true,
+ "type": "integer"
+ }
+}
"/checkouts/{checkout_id}/allows_renewal": {
"$ref": "paths/checkouts.json#/~1checkouts~1{checkout_id}~1allows_renewal"
},
+ "/advancededitormacros": {
+ "$ref": "paths/advancededitormacros.json#/~1advancededitormacros"
+ },
+ "/advancededitormacros/{advancededitormacro_id}": {
+ "$ref": "paths/advancededitormacros.json#/~1advancededitormacros~1{advancededitormacro_id}"
+ },
+ "/advancededitormacros/shared": {
+ "$ref": "paths/advancededitormacros.json#/~1advancededitormacros~1shared"
+ },
+ "/advancededitormacros/shared/{advancededitormacro_id}": {
+ "$ref": "paths/advancededitormacros.json#/~1advancededitormacros~1shared~1{advancededitormacro_id}"
+ },
"/patrons": {
"$ref": "paths/patrons.json#/~1patrons"
},
--- /dev/null
+{
+ "/advancededitormacros": {
+ "get": {
+ "x-mojo-to": "AdvancedEditorMacro#list",
+ "operationId": "listMacro",
+ "tags": ["advancededitormacro"],
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "name",
+ "in": "query",
+ "description": "Case insensative search on macro name",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "macro_text",
+ "in": "query",
+ "description": "Case insensative search on macro text",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "patron_id",
+ "in": "query",
+ "description": "Search on internal patron_id",
+ "required": false,
+ "type": "string"
+ },
+ {
+ "name": "shared",
+ "in": "query",
+ "description": "Search on shared macros",
+ "required": false,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "A list of macros",
+ "schema": {
+ "type": "array",
+ "items": {
+ "$ref": "../definitions.json#/advancededitormacro"
+ }
+ }
+ },
+ "403": {
+ "description": "Access forbidden",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "500": {
+ "description": "Internal error",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "503": {
+ "description": "Under maintenance",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ }
+ },
+ "x-koha-authorization": {
+ "permissions": {
+ "editcatalogue": "advanced_editor"
+ }
+ }
+ },
+ "post": {
+ "x-mojo-to": "AdvancedEditorMacro#add",
+ "operationId": "addAdvancedEditorMacro",
+ "tags": ["advancededitormacro"],
+ "parameters": [{
+ "name": "body",
+ "in": "body",
+ "description": "A JSON object containing informations about the new macro",
+ "required": true,
+ "schema": {
+ "$ref": "../definitions.json#/advancededitormacro"
+ }
+ }],
+ "produces": [
+ "application/json"
+ ],
+ "responses": {
+ "201": {
+ "description": "Macro added",
+ "schema": {
+ "$ref": "../definitions.json#/advancededitormacro"
+ }
+ },
+ "401": {
+ "description": "Authentication required",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "403": {
+ "description": "Access forbidden",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "500": {
+ "description": "Internal error",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "503": {
+ "description": "Under maintenance",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ }
+ },
+ "x-koha-authorization": {
+ "permissions": {
+ "editcatalogue": "advanced_editor"
+ }
+ }
+ }
+ },
+ "/advancededitormacros/shared": {
+ "post": {
+ "x-mojo-to": "AdvancedEditorMacro#add_shared",
+ "operationId": "addsharedAdvancedEditorMacro",
+ "tags": ["advancededitormacro"],
+ "parameters": [{
+ "name": "body",
+ "in": "body",
+ "description": "A JSON object containing informations about the new macro",
+ "required": true,
+ "schema": {
+ "$ref": "../definitions.json#/advancededitormacro"
+ }
+ }],
+ "produces": [
+ "application/json"
+ ],
+ "responses": {
+ "201": {
+ "description": "Macro added",
+ "schema": {
+ "$ref": "../definitions.json#/advancededitormacro"
+ }
+ },
+ "401": {
+ "description": "Authentication required",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "403": {
+ "description": "Access forbidden",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "500": {
+ "description": "Internal error",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "503": {
+ "description": "Under maintenance",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ }
+ },
+ "x-koha-authorization": {
+ "permissions": {
+ "editcatalogue": "advanced_editor",
+ "editcatalogue": "create_shared_macros"
+ }
+ }
+ }
+ },
+ "/advancededitormacros/{advancededitormacro_id}": {
+ "get": {
+ "x-mojo-to": "AdvancedEditorMacro#get",
+ "operationId": "getAdvancedEditorMacro",
+ "tags": ["advancededitormacros"],
+ "parameters": [{
+ "$ref": "../parameters.json#/advancededitormacro_id_pp"
+ }],
+ "produces": [
+ "application/json"
+ ],
+ "responses": {
+ "200": {
+ "description": "A macro",
+ "schema": {
+ "$ref": "../definitions.json#/advancededitormacro"
+ }
+ },
+ "403": {
+ "description": "Access forbidden",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "404": {
+ "description": "AdvancedEditorMacro not found",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "500": {
+ "description": "Internal error",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "503": {
+ "description": "Under maintenance",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ }
+ },
+ "x-koha-authorization": {
+ "permissions": {
+ "editcatalogue": "advanced_editor"
+ }
+ }
+ },
+ "put": {
+ "x-mojo-to": "AdvancedEditorMacro#update",
+ "operationId": "updateAdvancedEditorMacro",
+ "tags": ["advancededitormacros"],
+ "parameters": [{
+ "$ref": "../parameters.json#/advancededitormacro_id_pp"
+ }, {
+ "name": "body",
+ "in": "body",
+ "description": "An advanced editor macro object",
+ "required": true,
+ "schema": {
+ "$ref": "../definitions.json#/advancededitormacro"
+ }
+ }],
+ "produces": [
+ "application/json"
+ ],
+ "responses": {
+ "200": {
+ "description": "An advanced editor macro",
+ "schema": {
+ "$ref": "../definitions.json#/advancededitormacro"
+ }
+ },
+ "401": {
+ "description": "Authentication required",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "403": {
+ "description": "Access forbidden",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "404": {
+ "description": "Macro not found",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "500": {
+ "description": "Internal error",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "503": {
+ "description": "Under maintenance",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ }
+ },
+ "x-koha-authorization": {
+ "permissions": {
+ "editcatalogue": "advanced_editor"
+ }
+ }
+ },
+ "delete": {
+ "x-mojo-to": "AdvancedEditorMacro#delete",
+ "operationId": "deleteAdvancedEditorMacro",
+ "tags": ["advancededitormacros"],
+ "parameters": [{
+ "$ref": "../parameters.json#/advancededitormacro_id_pp"
+ }],
+ "produces": [
+ "application/json"
+ ],
+ "responses": {
+ "200": {
+ "description": "Advanced editor macro deleted",
+ "schema": {
+ "type": "string"
+ }
+ },
+ "401": {
+ "description": "Authentication required",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "403": {
+ "description": "Access forbidden",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "404": {
+ "description": "Macro not found",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "500": {
+ "description": "Internal error",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "503": {
+ "description": "Under maintenance",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ }
+ },
+ "x-koha-authorization": {
+ "permissions": {
+ "editcatalogue": "advanced_editor"
+ }
+ }
+ }
+ },
+ "/advancededitormacros/shared/{advancededitormacro_id}": {
+ "get": {
+ "x-mojo-to": "AdvancedEditorMacro#get_shared",
+ "operationId": "getsharedAdvancedEditorMacro",
+ "tags": ["advancededitormacros"],
+ "parameters": [{
+ "$ref": "../parameters.json#/advancededitormacro_id_pp"
+ }],
+ "produces": [
+ "application/json"
+ ],
+ "responses": {
+ "200": {
+ "description": "A macro",
+ "schema": {
+ "$ref": "../definitions.json#/advancededitormacro"
+ }
+ },
+ "403": {
+ "description": "Access forbidden",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "404": {
+ "description": "AdvancedEditorMacro not found",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "500": {
+ "description": "Internal error",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "503": {
+ "description": "Under maintenance",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ }
+ },
+ "x-koha-authorization": {
+ "permissions": {
+ "editcatalogue": "advanced_editor"
+ }
+ }
+ },
+ "put": {
+ "x-mojo-to": "AdvancedEditorMacro#update_shared",
+ "operationId": "updatesharedAdvancedEditorMacro",
+ "tags": ["advancededitormacros"],
+ "parameters": [{
+ "$ref": "../parameters.json#/advancededitormacro_id_pp"
+ }, {
+ "name": "body",
+ "in": "body",
+ "description": "An advanced editor macro object",
+ "required": true,
+ "schema": {
+ "$ref": "../definitions.json#/advancededitormacro"
+ }
+ }],
+ "produces": [
+ "application/json"
+ ],
+ "responses": {
+ "200": {
+ "description": "An advanced editor macro",
+ "schema": {
+ "$ref": "../definitions.json#/advancededitormacro"
+ }
+ },
+ "401": {
+ "description": "Authentication required",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "403": {
+ "description": "Access forbidden",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "404": {
+ "description": "Macro not found",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "500": {
+ "description": "Internal error",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "503": {
+ "description": "Under maintenance",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ }
+ },
+ "x-koha-authorization": {
+ "permissions": {
+ "editcatalogue": "advanced_editor",
+ "editcatalogue": "create_shared_macros"
+ }
+ }
+ },
+ "delete": {
+ "x-mojo-to": "AdvancedEditorMacro#delete_shared",
+ "operationId": "deletesharedAdvancedEditorMacro",
+ "tags": ["advancededitormacros"],
+ "parameters": [{
+ "$ref": "../parameters.json#/advancededitormacro_id_pp"
+ }],
+ "produces": [
+ "application/json"
+ ],
+ "responses": {
+ "200": {
+ "description": "Advanced editor macro deleted",
+ "schema": {
+ "type": "string"
+ }
+ },
+ "401": {
+ "description": "Authentication required",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "403": {
+ "description": "Access forbidden",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "404": {
+ "description": "Macro not found",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "500": {
+ "description": "Internal error",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ },
+ "503": {
+ "description": "Under maintenance",
+ "schema": {
+ "$ref": "../definitions.json#/error"
+ }
+ }
+ },
+ "x-koha-authorization": {
+ "permissions": {
+ "editcatalogue": "advanced_editor",
+ "editcatalogue": "delete_shared_macros"
+ }
+ }
+ }
+ }
+}
"type": "integer",
"description": "Internal biblio identifier"
},
+ "advancededitormacro_id": {
+ "type": "integer",
+ "description": "Internal advanced editor macro identifier",
+ "readOnly": true
+ },
"patron_id": {
"type": "integer",
"description": "Internal patron identifier"
--- /dev/null
+#!/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, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use Modern::Perl;
+
+use Test::More tests => 5;
+use Test::Mojo;
+use Test::Warn;
+
+use t::lib::TestBuilder;
+use t::lib::Mocks;
+
+use Koha::AdvancedEditorMacros;
+use Koha::Database;
+
+my $schema = Koha::Database->new->schema;
+my $builder = t::lib::TestBuilder->new;
+
+t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 );
+
+my $t = Test::Mojo->new('Koha::REST::V1');
+
+$schema->storage->txn_begin;
+
+subtest 'list() tests' => sub {
+ plan tests => 8;
+
+
+ my $patron_1 = $builder->build_object({
+ class => 'Koha::Patrons',
+ value => { flags => 9 }
+ });
+ my $patron_2 = $builder->build_object({
+ class => 'Koha::Patrons',
+ });
+ my $password = 'thePassword123';
+ $patron_1->set_password({ password => $password, skip_validation => 1 });
+ my $userid = $patron_1->userid;
+
+ # Create test context
+ my $macro_1 = $builder->build_object({ class => 'Koha::AdvancedEditorMacros', value =>
+ {
+ name => 'Test1',
+ macro => 'delete 100',
+ borrowernumber => $patron_1->borrowernumber,
+ }
+ });
+ my $macro_2 = $builder->build_object({ class => 'Koha::AdvancedEditorMacros', value =>
+ {
+ name => 'Test2',
+ macro => 'delete 100',
+ borrowernumber => $patron_1->borrowernumber,
+ shared=> 1,
+ }
+ });
+ my $macro_3 = $builder->build_object({ class => 'Koha::AdvancedEditorMacros', value =>
+ {
+ name => 'Test3',
+ macro => 'delete 100',
+ borrowernumber => $patron_2->borrowernumber,
+ }
+ });
+ my $macro_4 = $builder->build_object({ class => 'Koha::AdvancedEditorMacros', value =>
+ {
+ name => 'Test4',
+ macro => 'delete 100',
+ borrowernumber => $patron_2->borrowernumber,
+ shared => 1,
+ }
+ });
+
+ my $macros_index = Koha::AdvancedEditorMacros->search({ -or => { shared => 1, borrowernumber => $patron_1->borrowernumber } })->count-1;
+ ## Authorized user tests
+ # Make sure we are returned with the correct amount of macros
+ $t->get_ok( "//$userid:$password@/api/v1/advancededitormacros" )
+ ->status_is( 200, 'SWAGGER3.2.2' )
+ ->json_has('/' . $macros_index . '/macro_id')
+ ->json_hasnt('/' . ($macros_index + 1) . '/macro_id');
+
+ subtest 'query parameters' => sub {
+
+ plan tests => 15;
+ $t->get_ok("//$userid:$password@/api/v1/advancededitormacros?name=" . $macro_2->name)
+ ->status_is(200)
+ ->json_has( [ $macro_2 ] );
+ $t->get_ok("//$userid:$password@/api/v1/advancededitormacros?name=" . $macro_3->name)
+ ->status_is(200)
+ ->json_has( [ ] );
+ $t->get_ok("//$userid:$password@/api/v1/advancededitormacros?macro_text=delete 100")
+ ->status_is(200)
+ ->json_has( [ $macro_1, $macro_2, $macro_4 ] );
+ $t->get_ok("//$userid:$password@/api/v1/advancededitormacros?patron_id=" . $patron_1->borrowernumber)
+ ->status_is(200)
+ ->json_has( [ $macro_1, $macro_2 ] );
+ $t->get_ok("//$userid:$password@/api/v1/advancededitormacros?shared=1")
+ ->status_is(200)
+ ->json_has( [ $macro_2, $macro_4 ] );
+ };
+
+ # Warn on unsupported query parameter
+ $t->get_ok( "//$userid:$password@/api/v1/advancededitormacros?macro_blah=blah" )
+ ->status_is(400)
+ ->json_is( [{ path => '/query/macro_blah', message => 'Malformed query string'}] );
+
+};
+
+subtest 'get() tests' => sub {
+
+ plan tests => 15;
+
+ my $patron = $builder->build_object({
+ class => 'Koha::Patrons',
+ value => { flags => 1 }
+ });
+ my $password = 'thePassword123';
+ $patron->set_password({ password => $password, skip_validation => 1 });
+ my $userid = $patron->userid;
+
+ my $macro_1 = $builder->build_object( { class => 'Koha::AdvancedEditorMacros', value => {
+ shared => 1,
+ }
+ });
+ my $macro_2 = $builder->build_object( { class => 'Koha::AdvancedEditorMacros', value => {
+ shared => 0,
+ }
+ });
+ my $macro_3 = $builder->build_object( { class => 'Koha::AdvancedEditorMacros', value => {
+ borrowernumber => $patron->borrowernumber,
+ shared => 0,
+ }
+ });
+
+ $t->get_ok( "//$userid:$password@/api/v1/advancededitormacros/" . $macro_1->id )
+ ->status_is( 403, 'Cannot get a shared macro via regular endpoint' )
+ ->json_is( '/error' => 'This macro is shared, you must access it via advancededitormacros/shared' );
+
+ $t->get_ok( "//$userid:$password@/api/v1/advancededitormacros/shared/" . $macro_1->id )
+ ->status_is( 200, 'Can get a shared macro via shared endpoint' )
+ ->json_is( '' => Koha::REST::V1::AdvancedEditorMacro::_to_api( $macro_1->TO_JSON ), 'Macro correctly retrieved' );
+
+ $t->get_ok( "//$userid:$password@/api/v1/advancededitormacros/" . $macro_2->id )
+ ->status_is( 403, 'Cannot access another users macro' )
+ ->json_is( '/error' => 'You do not have permission to access this macro' );
+
+ $t->get_ok( "//$userid:$password@/api/v1/advancededitormacros/" . $macro_3->id )
+ ->status_is( 200, 'Can get your own private macro' )
+ ->json_is( '' => Koha::REST::V1::AdvancedEditorMacro::_to_api( $macro_3->TO_JSON ), 'Macro correctly retrieved' );
+
+ my $non_existent_code = $macro_1->id;
+ $macro_1->delete;
+
+ $t->get_ok( "//$userid:$password@/api/v1/advancededitormacros/" . $non_existent_code )
+ ->status_is(404)
+ ->json_is( '/error' => 'Macro not found' );
+
+};
+
+subtest 'add() tests' => sub {
+
+ plan tests => 24;
+
+ my $authorized_patron = $builder->build_object({
+ class => 'Koha::Patrons',
+ value => { flags => 0 }
+ });
+ $builder->build({
+ source => 'UserPermission',
+ value => {
+ borrowernumber => $authorized_patron->borrowernumber,
+ module_bit => 9,
+ code => 'advanced_editor',
+ },
+ });
+
+ my $password = 'thePassword123';
+ $authorized_patron->set_password({ password => $password, skip_validation => 1 });
+ my $auth_userid = $authorized_patron->userid;
+
+ my $unauthorized_patron = $builder->build_object({
+ class => 'Koha::Patrons',
+ value => { flags => 0 }
+ });
+ $unauthorized_patron->set_password({ password => $password, skip_validation => 1 });
+ my $unauth_userid = $unauthorized_patron->userid;
+
+ my $macro = $builder->build_object({
+ class => 'Koha::AdvancedEditorMacros',
+ value => { shared => 0 }
+ });
+ my $macro_values = Koha::REST::V1::AdvancedEditorMacro::_to_api( $macro->TO_JSON );
+ delete $macro_values->{macro_id};
+ $macro->delete;
+
+ # Unauthorized attempt to write
+ $t->post_ok( "//$unauth_userid:$password@/api/v1/advancededitormacros" => json => $macro_values )
+ ->status_is(403);
+
+ # Authorized attempt to write invalid data
+ my $macro_with_invalid_field = { %$macro_values };
+ $macro_with_invalid_field->{'big_mac_ro'} = 'Mac attack';
+
+ $t->post_ok( "//$auth_userid:$password@/api/v1/advancededitormacros" => json => $macro_with_invalid_field )
+ ->status_is(400)
+ ->json_is(
+ "/errors" => [
+ {
+ message => "Properties not allowed: big_mac_ro.",
+ path => "/body"
+ }
+ ]
+ );
+
+ # Authorized attempt to write
+ $t->post_ok( "//$auth_userid:$password@/api/v1/advancededitormacros" => json => $macro_values )
+ ->status_is( 201, 'SWAGGER3.2.1' )
+ ->json_has( '/macro_id', 'We generated a new id' )
+ ->json_is( '/name' => $macro_values->{name}, 'The name matches what we supplied' )
+ ->json_is( '/macro_text' => $macro_values->{macro_text}, 'The text matches what we supplied' )
+ ->json_is( '/patron_id' => $macro_values->{patron_id}, 'The borrower matches the borrower who submitted' )
+ ->json_is( '/shared' => 0, 'The macro is not shared' )
+ ->header_like( Location => qr|^\/api\/v1\/advancededitormacros\/d*|, 'Correct location' );
+
+ # save the library_id
+ my $macro_id = 999;
+
+ # Authorized attempt to create with existing id
+ $macro_values->{macro_id} = $macro_id;
+
+ $t->post_ok( "//$auth_userid:$password@/api/v1/advancededitormacros" => json => $macro_values )
+ ->status_is(400)
+ ->json_is( '/errors' => [
+ {
+ message => "Read-only.",
+ path => "/body/macro_id"
+ }
+ ]
+ );
+
+ $macro_values->{shared} = 1;
+ delete $macro_values->{macro_id};
+
+ # Unauthorized attempt to write a shared macro on private endpoint
+ $t->post_ok( "//$auth_userid:$password@/api/v1/advancededitormacros" => json => $macro_values )
+ ->status_is(403);
+ # Unauthorized attempt to write a private macro on shared endpoint
+ $t->post_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/shared" => json => $macro_values )
+ ->status_is(403);
+
+ $builder->build({
+ source => 'UserPermission',
+ value => {
+ borrowernumber => $authorized_patron->borrowernumber,
+ module_bit => 9,
+ code => 'create_shared_macros',
+ },
+ });
+
+ # Authorized attempt to write a shared macro on private endpoint
+ $t->post_ok( "//$auth_userid:$password@/api/v1/advancededitormacros" => json => $macro_values )
+ ->status_is(403);
+
+ # Authorized attempt to write a shared macro on shared endpoint
+ $t->post_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/shared" => json => $macro_values )
+ ->status_is(201);
+
+};
+
+subtest 'update() tests' => sub {
+ plan tests => 32;
+
+ my $authorized_patron = $builder->build_object({
+ class => 'Koha::Patrons',
+ value => { flags => 0 }
+ });
+ $builder->build({
+ source => 'UserPermission',
+ value => {
+ borrowernumber => $authorized_patron->borrowernumber,
+ module_bit => 9,
+ code => 'advanced_editor',
+ },
+ });
+
+ my $password = 'thePassword123';
+ $authorized_patron->set_password({ password => $password, skip_validation => 1 });
+ my $auth_userid = $authorized_patron->userid;
+
+ my $unauthorized_patron = $builder->build_object({
+ class => 'Koha::Patrons',
+ value => { flags => 0 }
+ });
+ $unauthorized_patron->set_password({ password => $password, skip_validation => 1 });
+ my $unauth_userid = $unauthorized_patron->userid;
+
+ my $macro = $builder->build_object({
+ class => 'Koha::AdvancedEditorMacros',
+ value => { borrowernumber => $authorized_patron->borrowernumber, shared => 0 }
+ });
+ my $macro_2 = $builder->build_object({
+ class => 'Koha::AdvancedEditorMacros',
+ value => { borrowernumber => $unauthorized_patron->borrowernumber, shared => 0 }
+ });
+ my $macro_id = $macro->id;
+ my $macro_2_id = $macro_2->id;
+ my $macro_values = Koha::REST::V1::AdvancedEditorMacro::_to_api( $macro->TO_JSON );
+ delete $macro_values->{macro_id};
+
+ # Unauthorized attempt to update
+ $t->put_ok( "//$unauth_userid:$password@/api/v1/advancededitormacros/$macro_id"
+ => json => { name => 'New unauthorized name change' } )
+ ->status_is(403);
+
+ # Attempt partial update on a PUT
+ my $macro_with_missing_field = {
+ name => "Call it macro-roni",
+ };
+
+ $t->put_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/$macro_id" => json => $macro_with_missing_field )
+ ->status_is(400)
+ ->json_has( "/errors" =>
+ [ { message => "Missing property.", path => "/body/macro_text" } ]
+ );
+
+ my $macro_update = {
+ name => "Macro-update",
+ macro_text => "delete 100",
+ patron_id => $authorized_patron->borrowernumber,
+ shared => 0,
+ };
+
+ my $test = $t->put_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/$macro_id" => json => $macro_update )
+ ->status_is(200, 'Authorized user can update a macro')
+ ->json_is( '/macro_id' => $macro_id, 'We get the id back' )
+ ->json_is( '/name' => $macro_update->{name}, 'We get the name back' )
+ ->json_is( '/macro_text' => $macro_update->{macro_text}, 'We get the text back' )
+ ->json_is( '/patron_id' => $macro_update->{patron_id}, 'We get the patron_id back' )
+ ->json_is( '/shared' => $macro_update->{shared}, 'It should still not be shared' );
+
+ # Now try to make the macro shared
+ $macro_update->{shared} = 1;
+
+ $t->put_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/shared/$macro_id" => json => $macro_update )
+ ->status_is(403, 'Cannot make your macro shared on private endpoint');
+ $t->put_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/shared/$macro_id" => json => $macro_update )
+ ->status_is(403, 'Cannot make your macro shared without permission');
+
+ $builder->build({
+ source => 'UserPermission',
+ value => {
+ borrowernumber => $authorized_patron->borrowernumber,
+ module_bit => 9,
+ code => 'create_shared_macros',
+ },
+ });
+
+ $t->put_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/$macro_id" => json => $macro_update )
+ ->status_is(403, 'Cannot make your macro shared on the private endpoint');
+
+ $t->put_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/shared/$macro_id" => json => $macro_update )
+ ->status_is(200, 'Can update macro to shared with permission')
+ ->json_is( '/macro_id' => $macro_id, 'We get back the id' )
+ ->json_is( '/name' => $macro_update->{name}, 'We get back the name' )
+ ->json_is( '/macro_text' => $macro_update->{macro_text}, 'We get back the text' )
+ ->json_is( '/patron_id' => $macro_update->{patron_id}, 'We get back our patron id' )
+ ->json_is( '/shared' => 1, 'It is shared' );
+
+ # Authorized attempt to write invalid data
+ my $macro_with_invalid_field = { %$macro_update };
+ $macro_with_invalid_field->{'big_mac_ro'} = 'Mac attack';
+
+ $t->put_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/$macro_id" => json => $macro_with_invalid_field )
+ ->status_is(400)
+ ->json_is(
+ "/errors" => [
+ {
+ message => "Properties not allowed: big_mac_ro.",
+ path => "/body"
+ }
+ ]
+ );
+
+ my $non_existent_macro = $builder->build_object({class => 'Koha::AdvancedEditorMacros'});
+ my $non_existent_code = $non_existent_macro->id;
+ $non_existent_macro->delete;
+
+ $t->put_ok("//$auth_userid:$password@/api/v1/advancededitormacros/$non_existent_code" => json => $macro_update)
+ ->status_is(404);
+
+ $t->put_ok("//$auth_userid:$password@/api/v1/advancededitormacros/$macro_2_id" => json => $macro_update)
+ ->status_is(403, "Cannot update other borrowers private macro");
+};
+
+subtest 'delete() tests' => sub {
+ plan tests => 12;
+
+ my $authorized_patron = $builder->build_object({
+ class => 'Koha::Patrons',
+ value => { flags => 0 }
+ });
+ $builder->build({
+ source => 'UserPermission',
+ value => {
+ borrowernumber => $authorized_patron->borrowernumber,
+ module_bit => 9,
+ code => 'advanced_editor',
+ },
+ });
+
+ my $password = 'thePassword123';
+ $authorized_patron->set_password({ password => $password, skip_validation => 1 });
+ my $auth_userid = $authorized_patron->userid;
+
+ my $unauthorized_patron = $builder->build_object({
+ class => 'Koha::Patrons',
+ value => { flags => 0 }
+ });
+ $unauthorized_patron->set_password({ password => $password, skip_validation => 1 });
+ my $unauth_userid = $unauthorized_patron->userid;
+
+ my $macro = $builder->build_object({
+ class => 'Koha::AdvancedEditorMacros',
+ value => { borrowernumber => $authorized_patron->borrowernumber, shared => 0 }
+ });
+ my $macro_2 = $builder->build_object({
+ class => 'Koha::AdvancedEditorMacros',
+ value => { borrowernumber => $unauthorized_patron->borrowernumber, shared => 0 }
+ });
+ my $macro_id = $macro->id;
+ my $macro_2_id = $macro_2->id;
+
+ # Unauthorized attempt to delete
+ $t->delete_ok( "//$unauth_userid:$password@/api/v1/advancededitormacros/$macro_2_id")
+ ->status_is(403, "Cannot delete macro without permission");
+
+ $t->delete_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/$macro_id")
+ ->status_is(200, 'Can delete macro with permission');
+
+ $t->delete_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/$macro_2_id")
+ ->status_is(403, 'Cannot delete other users macro with permission');
+
+ $macro_2->shared(1)->store();
+
+ $t->delete_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/shared/$macro_2_id")
+ ->status_is(403, 'Cannot delete other users shared macro without permission');
+
+ $builder->build({
+ source => 'UserPermission',
+ value => {
+ borrowernumber => $authorized_patron->borrowernumber,
+ module_bit => 9,
+ code => 'delete_shared_macros',
+ },
+ });
+ $t->delete_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/$macro_2_id")
+ ->status_is(403, 'Cannot delete other users shared macro with permission on private endpoint');
+ $t->delete_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/shared/$macro_2_id")
+ ->status_is(200, 'Can delete other users shared macro with permission');
+
+};
+
+$schema->storage->txn_rollback;