Bugzilla – Attachment 114556 Details for
Bug 27251
Rewrite the QOTD editor using the Koha REST API
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 27251: Rewrite QOTD with the Koha REST API
Bug-27251-Rewrite-QOTD-with-the-Koha-REST-API.patch (text/plain), 53.48 KB, created by
Jonathan Druart
on 2020-12-21 13:58:20 UTC
(
hide
)
Description:
Bug 27251: Rewrite QOTD with the Koha REST API
Filename:
MIME Type:
Creator:
Jonathan Druart
Created:
2020-12-21 13:58:20 UTC
Size:
53.48 KB
patch
obsolete
>From e91b1e874c3b4806b00fee5a368723c4c4a1a7d9 Mon Sep 17 00:00:00 2001 >From: Jonathan Druart <jonathan.druart@bugs.koha-community.org> >Date: Wed, 16 Dec 2020 12:34:43 +0100 >Subject: [PATCH] Bug 27251: Rewrite QOTD with the Koha REST API > >This patch replace the QOTD editor with our new way to CRUD the >adminitration page (like libraries and STMP servers) > >Test plan: >Play with the QOTD by adding, removing, updating quotes >Try to find bugs :) > >Bug 27251: Fix capitalization >--- > Koha/Quote.pm | 18 +- > Koha/REST/V1/Quotes.pm | 139 +++++++ > api/v1/swagger/definitions.json | 3 + > api/v1/swagger/definitions/quote.json | 22 + > api/v1/swagger/parameters.json | 3 + > api/v1/swagger/parameters/quote.json | 9 + > api/v1/swagger/paths.json | 6 + > api/v1/swagger/paths/quotes.json | 327 +++++++++++++++ > api/v1/swagger/x-primitives.json | 6 +- > .../prog/en/includes/quotes-toolbar.inc | 5 - > .../en/includes/quotes-upload-toolbar.inc | 5 - > .../prog/en/modules/intranet-main.tt | 2 +- > .../prog/en/modules/tools/quotes-upload.tt | 82 ++-- > .../prog/en/modules/tools/quotes.tt | 378 +++++++++--------- > tools/quotes.pl | 77 +++- > tools/quotes/quotes-upload_ajax.pl | 65 --- > tools/quotes/quotes_ajax.pl | 109 ----- > 17 files changed, 833 insertions(+), 423 deletions(-) > create mode 100644 Koha/REST/V1/Quotes.pm > create mode 100644 api/v1/swagger/definitions/quote.json > create mode 100644 api/v1/swagger/parameters/quote.json > create mode 100644 api/v1/swagger/paths/quotes.json > delete mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/quotes-toolbar.inc > delete mode 100644 koha-tmpl/intranet-tmpl/prog/en/includes/quotes-upload-toolbar.inc > delete mode 100755 tools/quotes/quotes-upload_ajax.pl > delete mode 100755 tools/quotes/quotes_ajax.pl > >diff --git a/Koha/Quote.pm b/Koha/Quote.pm >index acb844d3af..49868aa2a3 100644 >--- a/Koha/Quote.pm >+++ b/Koha/Quote.pm >@@ -33,6 +33,22 @@ Koha::Quote - Koha Quote object class > > =cut > >+=head3 to_api_mapping >+ >+This method returns the mapping for representing a Koha::Quote object >+on the API. >+ >+=cut >+ >+sub to_api_mapping { >+ return { >+ id => 'quote_id', >+ source => 'source', >+ text => 'text', >+ timestamp => 'displayed_on', >+ }; >+} >+ > =head3 _type > > =cut >@@ -41,4 +57,4 @@ sub _type { > return 'Quote'; > } > >-1; >\ No newline at end of file >+1; >diff --git a/Koha/REST/V1/Quotes.pm b/Koha/REST/V1/Quotes.pm >new file mode 100644 >index 0000000000..49c6e4cab6 >--- /dev/null >+++ b/Koha/REST/V1/Quotes.pm >@@ -0,0 +1,139 @@ >+package Koha::REST::V1::Quotes; >+ >+# 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 <http://www.gnu.org/licenses>. >+ >+use Modern::Perl; >+ >+use Mojo::Base 'Mojolicious::Controller'; >+ >+use Koha::Quotes; >+ >+use Try::Tiny; >+ >+=head1 API >+ >+=head2 Methods >+ >+=head3 list >+ >+=cut >+ >+sub list { >+ my $c = shift->openapi->valid_input or return; >+ >+ return try { >+ my $quotes_set = Koha::Quotes->new; >+ my $quotes = $c->objects->search( $quotes_set ); >+ return $c->render( status => 200, openapi => $quotes ); >+ } >+ catch { >+ $c->unhandled_exception($_); >+ }; >+ >+} >+ >+=head3 get >+ >+=cut >+ >+sub get { >+ my $c = shift->openapi->valid_input or return; >+ >+ return try { >+ my $quote = Koha::Quotes->find( $c->validation->param('quote_id') ); >+ unless ($quote) { >+ return $c->render( status => 404, >+ openapi => { error => "quote not found" } ); >+ } >+ >+ return $c->render( status => 200, openapi => $quote->to_api ); >+ } >+ catch { >+ $c->unhandled_exception($_); >+ } >+} >+ >+=head3 add >+ >+=cut >+ >+sub add { >+ my $c = shift->openapi->valid_input or return; >+ >+ return try { >+ my $quote = Koha::Quote->new_from_api( $c->validation->param('body') ); >+ $quote->store; >+ $c->res->headers->location( $c->req->url->to_string . '/' . $quote->id ); >+ return $c->render( >+ status => 201, >+ openapi => $quote->to_api >+ ); >+ } >+ catch { >+ $c->unhandled_exception($_); >+ }; >+} >+ >+=head3 update >+ >+=cut >+ >+sub update { >+ my $c = shift->openapi->valid_input or return; >+ >+ my $quote = Koha::Quotes->find( $c->validation->param('quote_id') ); >+ >+ if ( not defined $quote ) { >+ return $c->render( status => 404, >+ openapi => { error => "Object not found" } ); >+ } >+ >+ return try { >+ $quote->set_from_api( $c->validation->param('body') ); >+ $quote->store(); >+ return $c->render( status => 200, openapi => $quote->to_api ); >+ } >+ catch { >+ $c->unhandled_exception($_); >+ }; >+} >+ >+=head3 delete >+ >+=cut >+ >+sub delete { >+ my $c = shift->openapi->valid_input or return; >+ >+ my $quote = Koha::Quotes->find( $c->validation->param('quote_id') ); >+ if ( not defined $quote ) { >+ return $c->render( status => 404, >+ openapi => { error => "Object not found" } ); >+ } >+ >+ return try { >+ $quote->delete; >+ return $c->render( >+ status => 204, >+ openapi => q{} >+ ); >+ } >+ catch { >+ $c->unhandled_exception($_); >+ }; >+} >+ >+1; >diff --git a/api/v1/swagger/definitions.json b/api/v1/swagger/definitions.json >index e35054a456..bc844bdc8f 100644 >--- a/api/v1/swagger/definitions.json >+++ b/api/v1/swagger/definitions.json >@@ -65,6 +65,9 @@ > "patron_balance": { > "$ref": "definitions/patron_balance.json" > }, >+ "quote": { >+ "$ref": "definitions/quote.json" >+ }, > "allows_renewal": { > "$ref": "definitions/allows_renewal.json" > }, >diff --git a/api/v1/swagger/definitions/quote.json b/api/v1/swagger/definitions/quote.json >new file mode 100644 >index 0000000000..8033f2af66 >--- /dev/null >+++ b/api/v1/swagger/definitions/quote.json >@@ -0,0 +1,22 @@ >+{ >+ "type": "object", >+ "properties": { >+ "quote_id": { >+ "$ref": "../x-primitives.json#/quote_id" >+ }, >+ "source": { >+ "description": "source of the quote", >+ "type": "string" >+ }, >+ "text": { >+ "description": "text", >+ "type": ["string", "null"] >+ }, >+ "displayed_on": { >+ "description": "Last display date", >+ "type": ["string", "null"] >+ } >+ }, >+ "additionalProperties": false, >+ "required": ["quote_id", "source", "text"] >+} >diff --git a/api/v1/swagger/parameters.json b/api/v1/swagger/parameters.json >index ddced814d0..8fc892a6af 100644 >--- a/api/v1/swagger/parameters.json >+++ b/api/v1/swagger/parameters.json >@@ -32,6 +32,9 @@ > "order_id_pp": { > "$ref": "parameters/order.json#/order_id_pp" > }, >+ "quote_id_pp": { >+ "$ref": "parameters/quote.json#/quote_id_pp" >+ }, > "smtp_server_id_pp": { > "$ref": "parameters/smtp_server.json#/smtp_server_id_pp" > }, >diff --git a/api/v1/swagger/parameters/quote.json b/api/v1/swagger/parameters/quote.json >new file mode 100644 >index 0000000000..c7f36fcbc3 >--- /dev/null >+++ b/api/v1/swagger/parameters/quote.json >@@ -0,0 +1,9 @@ >+{ >+ "quote_id_pp": { >+ "name": "quote_id", >+ "in": "path", >+ "description": "Quote internal identifier", >+ "required": true, >+ "type": "integer" >+ } >+} >diff --git a/api/v1/swagger/paths.json b/api/v1/swagger/paths.json >index 3b5dc352d5..e046c57a22 100644 >--- a/api/v1/swagger/paths.json >+++ b/api/v1/swagger/paths.json >@@ -134,6 +134,12 @@ > "/public/patrons/{patron_id}/guarantors/can_see_checkouts": { > "$ref": "paths/public_patrons.json#/~1public~1patrons~1{patron_id}~1guarantors~1can_see_checkouts" > }, >+ "/quotes": { >+ "$ref": "paths/quotes.json#/~1quotes" >+ }, >+ "/quotes/{quote_id}": { >+ "$ref": "paths/quotes.json#/~1quotes~1{quote_id}" >+ }, > "/return_claims": { > "$ref": "paths/return_claims.json#/~1return_claims" > }, >diff --git a/api/v1/swagger/paths/quotes.json b/api/v1/swagger/paths/quotes.json >new file mode 100644 >index 0000000000..ac2a503fe0 >--- /dev/null >+++ b/api/v1/swagger/paths/quotes.json >@@ -0,0 +1,327 @@ >+{ >+ "/quotes": { >+ "get": { >+ "x-mojo-to": "Quotes#list", >+ "operationId": "listQuotes", >+ "tags": [ >+ "quotes" >+ ], >+ "produces": [ >+ "application/json" >+ ], >+ "parameters": [ >+ { >+ "name": "quote_id", >+ "in": "query", >+ "description": "Case insensitive search on quote id", >+ "required": false, >+ "type": "string" >+ }, >+ { >+ "name": "source", >+ "in": "query", >+ "description": "Case insensitive search on source", >+ "required": false, >+ "type": "string" >+ }, >+ { >+ "name": "text", >+ "in": "query", >+ "description": "Case insensitive search on text", >+ "required": false, >+ "type": "string" >+ }, >+ { >+ "name": "displayed_on", >+ "in": "query", >+ "description": "Case Insensative search on last displayed date", >+ "required": false, >+ "type": "string" >+ }, >+ { >+ "$ref": "../parameters.json#/match" >+ }, >+ { >+ "$ref": "../parameters.json#/order_by" >+ }, >+ { >+ "$ref": "../parameters.json#/page" >+ }, >+ { >+ "$ref": "../parameters.json#/per_page" >+ }, >+ { >+ "$ref": "../parameters.json#/q_param" >+ }, >+ { >+ "$ref": "../parameters.json#/q_body" >+ }, >+ { >+ "$ref": "../parameters.json#/q_header" >+ } >+ ], >+ "responses": { >+ "200": { >+ "description": "A list of quotes", >+ "schema": { >+ "type": "array", >+ "items": { >+ "$ref": "../definitions.json#/quote" >+ } >+ } >+ }, >+ "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": { >+ "catalogue": "1" >+ } >+ } >+ }, >+ "post": { >+ "x-mojo-to": "Quotes#add", >+ "operationId": "addQuote", >+ "tags": [ >+ "quotes" >+ ], >+ "parameters": [ >+ { >+ "name": "body", >+ "in": "body", >+ "description": "A JSON object containing informations about the new quote", >+ "required": true, >+ "schema": { >+ "$ref": "../definitions.json#/quote" >+ } >+ } >+ ], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "201": { >+ "description": "Quote added", >+ "schema": { >+ "$ref": "../definitions.json#/quote" >+ } >+ }, >+ "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": { >+ "tools": "edit_quotes" >+ } >+ } >+ } >+ }, >+ "/quotes/{quote_id}": { >+ "get": { >+ "x-mojo-to": "Quotes#get", >+ "operationId": "getQuote", >+ "tags": [ >+ "quotes" >+ ], >+ "parameters": [ >+ { >+ "$ref": "../parameters.json#/quote_id_pp" >+ } >+ ], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "A Quote", >+ "schema": { >+ "$ref": "../definitions.json#/quote" >+ } >+ }, >+ "404": { >+ "description": "Quote 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": { >+ "catalogue": "1" >+ } >+ } >+ }, >+ "put": { >+ "x-mojo-to": "Quotes#update", >+ "operationId": "updateQuote", >+ "tags": [ >+ "quotes" >+ ], >+ "parameters": [ >+ { >+ "$ref": "../parameters.json#/quote_id_pp" >+ }, >+ { >+ "name": "body", >+ "in": "body", >+ "description": "a quote object", >+ "required": true, >+ "schema": { >+ "$ref": "../definitions.json#/quote" >+ } >+ } >+ ], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "A quote", >+ "schema": { >+ "$ref": "../definitions.json#/quote" >+ } >+ }, >+ "401": { >+ "description": "Authentication required", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "403": { >+ "description": "Access forbidden", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "404": { >+ "description": "Quote 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": { >+ "tools": "edit_quotes" >+ } >+ } >+ }, >+ "delete": { >+ "x-mojo-to": "Quotes#delete", >+ "operationId": "deleteQuote", >+ "tags": [ >+ "quotes" >+ ], >+ "parameters": [ >+ { >+ "$ref": "../parameters.json#/quote_id_pp" >+ } >+ ], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "204": { >+ "description": "Quote deleted" >+ }, >+ "401": { >+ "description": "Authentication required", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "403": { >+ "description": "Access forbidden", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ }, >+ "404": { >+ "description": "Quote 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": { >+ "tools": "edit_quotes" >+ } >+ } >+ } >+ } >+} >diff --git a/api/v1/swagger/x-primitives.json b/api/v1/swagger/x-primitives.json >index 0594264be9..13cc1da4fc 100644 >--- a/api/v1/swagger/x-primitives.json >+++ b/api/v1/swagger/x-primitives.json >@@ -52,6 +52,10 @@ > "type": "integer", > "description": "internally assigned fund identifier", > "readOnly": true >+ }, >+ "quote_id": { >+ "type": "integer", >+ "description": "internally assigned quote identifier", >+ "readOnly": true > } >- > } >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/quotes-toolbar.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/quotes-toolbar.inc >deleted file mode 100644 >index e63ad8b450..0000000000 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/quotes-toolbar.inc >+++ /dev/null >@@ -1,5 +0,0 @@ >-<div id="toolbar" class="btn-toolbar"> >- <div class="btn-group"><a class="btn btn-default" id="add_quote" href="#"><i class="fa fa-plus"></i> Add quote</a></div> >- <div class="btn-group"><a class="btn btn-default" id="delete_quote" href="#"><i class="fa fa-trash"></i> Delete quote(s)</a></div> >- <div class="btn-group"><a class="btn btn-default" id="import_quotes" href="/cgi-bin/koha/tools/quotes-upload.pl"><i class="fa fa-folder-open"></i> Import quotes</a></div> >-</div> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/quotes-upload-toolbar.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/quotes-upload-toolbar.inc >deleted file mode 100644 >index b531be0452..0000000000 >--- a/koha-tmpl/intranet-tmpl/prog/en/includes/quotes-upload-toolbar.inc >+++ /dev/null >@@ -1,5 +0,0 @@ >-<div id="toolbar" class="btn-toolbar" style="visibility: hidden; position: absolute"> >- <div class="btn-group"><a class="btn btn-default" id="save_quotes" href="#"><i class="fa fa-save"></i> Save quotes</a></div> >- <div class="btn-group"><a class="btn btn-default" id="delete_quote" href="#"><i class="fa fa-trash"></i> Delete quote(s)</a></div> >- <div class="btn-group"><a href="/cgi-bin/koha/tools/quotes-upload.pl" id="cancel_quotes" class="btn btn-default"><i class="fa fa-remove"></i> Cancel import</a></div> >-</div> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt >index 373c312dd5..0f06f70797 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt >@@ -37,7 +37,7 @@ > [% END %] > [% IF ( daily_quote ) %] > <div id="area-news"> >- <h3>Quote of the Day</h3> >+ <h3>Quote of the day</h3> > <div class="newsitem"> > <span id="daily-quote-text">[% daily_quote.text | html %]</span><span id="daily-quote-sep"> ~ </span><span id="daily-quote-source">[% daily_quote.source | html %]</span> > </div> >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/quotes-upload.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/quotes-upload.tt >index dc5da4b0f2..93a5d25e96 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/quotes-upload.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/quotes-upload.tt >@@ -18,9 +18,18 @@ > <div class="row"> > <div class="col-sm-10 col-sm-push-2"> > <main> >+ <div id="toolbar" class="btn-toolbar" style="visibility: hidden; position: absolute"> >+ <div class="btn-group"><a class="btn btn-default" id="save_quotes" href="#"><i class="fa fa-save"></i> Save quotes</a></div> >+ <div class="btn-group"><a href="/cgi-bin/koha/tools/quotes-upload.pl" id="cancel_quotes" class="btn btn-default"><i class="fa fa-remove"></i> Cancel import</a></div> >+ </div> > >- [% INCLUDE 'quotes-upload-toolbar.inc' %] > <h2>Quote uploader</h2> >+ >+ <div id="messages" style="display: none;"> >+ <div class="import_success dialog message" style="display: none;"></div> >+ <div class="import_errors dialog alert" style="display: none;"></div> >+ </div> >+ > <div id="instructions"> > <fieldset id="file_uploader_help" class="rows"> > <legend>Instructions</legend> >@@ -34,12 +43,12 @@ > <div id="file_editor_inst"> > <ul> > <li>Click on any field to edit the contents; Press the <Enter> key to save edit.</li> >- <li>Click on one or more quote numbers to select entire quotes for deletion; Click the 'Delete Quote(s)' button to delete selected quotes.</li> > <li>Click the 'Save Quotes' button in the toolbar to save the entire batch of quotes.</li> > </ul> > </div> > </fieldset> > </div> >+ > <fieldset id="file_uploader" class="rows"> > <legend>Upload quotes</legend> > <div id="file_upload"> >@@ -232,7 +241,6 @@ > $('#file_uploader').css("top","-150px"); > $('#quotes_editor').css("visibility","visible"); > $("#save_quotes").on("click", yuiGetData); >- $("#delete_quote").on("click", fnClickDeleteRow); > > oTable = $('#quotes_editor').dataTable( { > "bAutoWidth" : false, >@@ -335,44 +343,44 @@ > > $('#file_upload').one('change', fnHandleFileSelect); > >- }); >- >+ var MSG_IMPORT_SUCCESS = _("%s quotes imported successfully"); >+ var MSG_IMPORT_ERROR = _("%s quotes have not been imported. An error occurred"); > function fnGetData(element) { >- var jqXHR = $.ajax({ >- url : "/cgi-bin/koha/tools/quotes/quotes-upload_ajax.pl", >- type : "POST", >- contentType : "application/x-www-form-urlencoded", // we must claim this mimetype or CGI will not decode the URL encoding >- dataType : "json", >- data : { >- "quote" : encodeURI ( JSON.stringify(oTable.fnGetData()) ), >- "action" : "add", >- }, >- success : function(){ >- var response = JSON.parse(jqXHR.responseText); >- if (response.success) { >- alert(_("%s quotes saved.").format(response.records)); >- window.location.reload(true); // is this the best route? >- } else { >- alert(_("%s quotes saved, but an error has occurred. Please ask your administrator to check the server log for more details.").format(response.records)); >- window.location.reload(true); // is this the best route? >- } >- }, >+ var lines = oTable.fnGetData(); >+ $(lines).each(function(line){ >+ var data = {source: this[1], text: this[2]}; >+ var success = 0; var error = 0; >+ $.ajax({ >+ url : "/api/v1/quotes", >+ method : "POST", >+ data : JSON.stringify(data), >+ dataType : "application/json", >+ success : function(data) { >+ $("#messages").show(); >+ var import_success = $("#messages .import_success"); >+ import_success.show(); >+ import_success.data("nb") >+ nb_success = import_success.data("nb") || 0; >+ nb_success++; >+ $("#messages .import_success").text(MSG_IMPORT_SUCCESS.format(nb_success)); >+ import_success.data("nb", nb_success); >+ }, >+ error : function(xhr) { >+ if (xhr.status==201) { this.success(null, "Created", xhr); return; } >+ $("#messages").show(); >+ var import_error = $("#messages .import_error"); >+ import_error.show(); >+ import_error.data("nb") >+ nb_error = import_error.data("nb") || 0; >+ nb_error++; >+ $("#messages .import_error").text(MSG_IMPORT_ERROR.format(nb_error)); >+ import_error.data("nb", nb_error); >+ }, >+ }); > }); > } > >- function fnClickDeleteRow() { >- var idsToDelete = oTable.$('.selected').map(function() { >- return this.id; >- }).get().join(', '); >- if (!idsToDelete) { >- alert(_("Please select a quote(s) by clicking the quote id(s) you desire to delete.")); >- } >- else if (confirm(_("Are you sure you wish to delete quote(s) %s?").format(idsToDelete))) { >- oTable.$('.selected').each(function(){ >- oTable.fnDeleteRow(this); >- }); >- } >- } >+ }); // $(document).ready > </script> > [% END %] > >diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/quotes.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/quotes.tt >index 5339069a0f..c42839bfd4 100644 >--- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/quotes.tt >+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/quotes.tt >@@ -1,10 +1,10 @@ > [% USE raw %] > [% USE Asset %] > [% SET footerjs = 1 %] >- [% INCLUDE 'doc-head-open.inc' %] >- <title>Koha › Tools › Quote editor</title> >- [% INCLUDE 'doc-head-close.inc' %] >- [% Asset.css("css/quotes.css") | $raw %] >+[% INCLUDE 'doc-head-open.inc' %] >+<title>Koha › Tools › Quote editor</title> >+[% INCLUDE 'doc-head-close.inc' %] >+[% Asset.css("css/quotes.css") | $raw %] > </head> > > <body id="tools_quotes" class="tools"> >@@ -18,43 +18,110 @@ > <div class="col-sm-10 col-sm-push-2"> > <main> > >- [% INCLUDE 'quotes-toolbar.inc' %] >- <h2>Quote editor</h2> >- <div id="instructions"> >- <fieldset id="quote_editor_help" class="rows"> >- <legend>Instructions</legend> >- <div id="quote_editor_inst"> >- <ul> >- <li>Click on the 'Add quote' button to add a single quote; Press the <Enter> key to save the quote.<br /> >- <strong>Note: </strong>Both the 'source' and 'text' fields must have content in order for the quote to be saved.</li> >- <li>Click on any field to edit the contents; Press the <Enter> key to save edit.</li> >- <li>Click on one or more quote numbers to select entire quotes for deletion; Click the 'Delete quote(s)' button to delete selected quotes.</li> >- <li>Click the 'Import quotes' button in the toolbar to import a CSV file of quotes.</li> >- </ul> >- </div> >- </fieldset> >+[% FOREACH m IN messages %] >+ <div class="dialog [% m.type | html %]" id="quote_action_result_dialog"> >+ [% SWITCH m.code %] >+ [% CASE 'error_on_update' %] >+ An error occurred when updating this quote. Perhaps it already exists. >+ [% CASE 'error_on_insert' %] >+ An error occurred when adding this quote. >+ [% CASE 'success_on_update' %] >+ Quote updated successfully. >+ [% CASE 'success_on_insert' %] >+ Quote added successfully. >+ [% CASE %] >+ [% m.code | html %] >+ [% END %] >+ </div> >+[% END %] >+ >+ <div class="dialog message" id="quote_delete_success" style="display: none;"></div> >+ <div class="dialog alert" id="quote_delete_error" style="display: none;"></div> >+ >+[% IF op == 'list' %] >+ <div id="toolbar" class="btn-toolbar"> >+ <a class="btn btn-default" id="newquote" href="/cgi-bin/koha/tools/quotes.pl?op=add_form"><i class="fa fa-plus"></i> New quote</a> >+ <a class="btn btn-default" id="import_quotes" href="/cgi-bin/koha/tools/quotes-upload.pl"><i class="fa fa-folder-open"></i> Import quotes</a> >+ </div> >+[% END %] >+ >+[% IF op == 'add_form' %] >+ <h3>[% IF quote %]Modify quote[% ELSE %]New quote[% END %]</h3> >+ <form action="/cgi-bin/koha/tools/quotes.pl" id="Aform" name="Aform" class="validated" method="post"> >+ <fieldset class="rows"> >+ <input type="hidden" name="op" value="add_validate" /> >+ <ol> >+ <li> >+ <label for="text" class="required">Source: </label> >+ <input type="text" name="source" id="source" value="[% quote.source | html %]" class="required" required="required" /> >+ <span class="required">Required</span> >+ </li> >+ <li> >+ <label for="text" class="required">Text: </label> >+ <textarea name="text" id="text" class="required" required="required" />[% quote.text | html %]</textarea> >+ <span class="required">Required</span> >+ </li> >+ </ol> >+ </fieldset> >+ <fieldset class="action"> >+ <input type="hidden" name="id" value="[% quote.id %]" /> >+ <input type="submit" value="Submit" /> >+ <a class="cancel" href="/cgi-bin/koha/tools/quotes.pl">Cancel</a> >+ </fieldset> >+ </form> >+[% END %] >+ >+[% IF op == 'delete_confirm' %] >+ <div class="dialog alert"> >+ <form action="/cgi-bin/koha/tools/quotes.pl" method="post"> >+ <h3>Are you sure you want to delete the following quote?</h3> >+ [% quote.source | html %] - [% quote.text | html %] >+ <input type="hidden" name="op" value="delete_confirmed" /> >+ <input type="hidden" name="id" value="[% quote.id | html %]" /> >+ <button type="submit" class="approve"><i class="fa fa-fw fa-check"></i> Yes, delete</button> >+ </form> >+ <form action="/cgi-bin/koha/tools/quotes.pl" method="get"> >+ <button type="submit" class="deny"><i class="fa fa-fw fa-remove"></i> No, do not delete</button> >+ </form> >+ </div> >+[% END %] >+ >+[% IF op == 'list' %] >+ <h3>Quotes</h3> >+ [% IF quotes_count > 0 %] >+ <table id="quotes"> >+ <thead> >+ <tr> >+ <th>ID</th> >+ <th>Source</th> >+ <th>Text</th> >+ <th>Last display</th> >+ <th data-class-name="actions">Actions</th> >+ </tr> >+ </thead> >+ </table> >+ [% ELSE %] >+ <div class="dialog message">There are no quotes defined. <a href="/cgi-bin/koha/tools/quotes.pl?op=add_form">Start defining quotes</a>.</div> >+ [% END %] >+ >+ <div id="delete_confirm_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="delete_confirm_modal_label" aria-hidden="true"> >+ <div class="modal-dialog"> >+ <div class="modal-content"> >+ <div class="modal-header"> >+ <button type="button" class="closebtn" data-dismiss="modal" aria-hidden="true">Ã</button> >+ <h3 id="delete_confirm_modal_label">Delete quote</h3> > </div> >- <table id="quotes_editor"> >- <thead> >- <tr> >- <th><span style="cursor: help" id="id_help">ID</span></th> >- <th>Source</th> >- <th>Text</th> >- <th>Last displayed</th> >- </tr> >- </thead> >- <tbody> >- <!-- tbody content is generated by DataTables --> >- <tr> >- <td></td> >- <td></td> >- <td>Loading data...</td> >- <td></td> >- </tr> >- </tbody> >- </table> >- <fieldset id="footer" class="action"> >- </fieldset> >+ <div class="modal-body"> >+ <div id="delete_confirm_dialog"></div> >+ </div> >+ <div class="modal-footer"> >+ <a href="#" class="btn btn-default" id="delete_confirm_modal_button" role="button" data-toggle="modal">Delete</a> >+ <button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button> >+ </div> >+ </div> <!-- /.modal-content --> >+ </div> <!-- /.modal-dialog --> >+ </div> <!-- #delete_confirm_modal --> >+[% END %] > > </main> > </div> <!-- /.col-sm-10.col-sm-push-2 --> >@@ -68,164 +135,99 @@ > > [% MACRO jsinclude BLOCK %] > [% Asset.js("js/tools-menu.js") | $raw %] >+ [% INCLUDE 'js-date-format.inc' %] > [% INCLUDE 'datatables.inc' %] >- [% Asset.js("lib/jquery/plugins/dataTables.fnReloadAjax.js") | $raw %] >- [% Asset.js("lib/jquery/plugins/jquery.jeditable.mini.js") | $raw %] >+ > <script> >- var oTable; /* oTable needs to be global */ >- var sEmptyTable = _("No quotes available. Please use the 'Add quote' button to add a quote."); /* override the default message in datatables.inc */ > $(document).ready(function() { >- /* NOTE: This is an ajax-source datatable and *not* a server-side sourced datatable. */ >- /* See the datatable docs if you don't understand this difference. */ >- oTable = $("#quotes_editor").dataTable({ >- "bAutoWidth" : false, >- "bProcessing" : true, >- "bPaginate" : true, >- "sPaginationType" : "full_numbers", >- "sDom": '<"top pager"iflp>rt<"bottom pager"flp><"clear">', >- "sAjaxSource" : "/cgi-bin/koha/tools/quotes/quotes_ajax.pl", >- "aoColumns" : [ >- { "sWidth": "3%" }, >- { "sWidth": "11%" }, >- { "sWidth": "75%" }, >- { "sWidth": "11%" }, >- ], >- "oLanguage": dataTablesDefaults.oLanguage, >- "fnPreDrawCallback": function(oSettings) { >- return true; >+ >+ var quotes_url = '/api/v1/quotes'; >+ var quotes = $("#quotes").api({ >+ "ajax": { >+ "url": quotes_url > }, >- "fnRowCallback": function( nRow, aData, iDisplayIndex ) { >- /* do foo on the current row and its child nodes */ >- var noEditFields = []; >- var quoteID = $('td', nRow)[0].innerHTML; >- $(nRow).attr("id", quoteID); /* set row ids to quote id */ >- $('td:eq(0)', nRow).click(function() {$(this.parentNode).toggleClass('selected',this.clicked);}); /* add row selectors */ >- $('td:eq(0)', nRow).attr("title", _("Click ID to select/deselect quote")); >- $('td', nRow).attr("id",quoteID); /* FIXME: this is a bit of a hack */ >- if (isNaN(quoteID)) { >- noEditFields = [0,1,2,3]; /* all fields when adding a quote */ >- } else { >- noEditFields = [0,3]; /* id, timestamp */ >- } >- /* apply no_edit id to noEditFields */ >- for (i=0; i<noEditFields.length; i++) { >- $('td', nRow)[noEditFields[i]].setAttribute("id","no_edit"); >+ 'emptyTable': '<div class="dialog message">'+_("There are no quotes defined.")+' <a href="/cgi-bin/koha/tools/quotes.pl?op=add_form">'+_("Start defining quotes")+'</a>.</div>', >+ "columnDefs": [ { >+ "targets": [0,1,2,3], >+ "render": function (data, type, row, meta) { >+ if ( type == 'display' ) { >+ if ( data != null ) { >+ return data.escapeHtml(); >+ } >+ else { >+ return ""; >+ } >+ } >+ return data; > } >- return nRow; >- }, >- "fnDrawCallback": function(oSettings) { >- /* Apply the jEditable handlers to the table on all fields w/o the no_edit id */ >- $('#quotes_editor tbody td[id!="no_edit"]').editable( "/cgi-bin/koha/tools/quotes/quotes_ajax.pl", { >- "submitdata" : function ( value, settings ) { >- return { >- "column" : oTable.fnGetPosition( this )[2], >- "action" : "edit", >- }; >- }, >- "placeholder" : "Saving data...", >- }); >- }, >- }); >- $("#add_quote").click(function(){ >- fnClickAddRow(); >- return false; >- }); >- $("#delete_quote").click(function(){ >- fnClickDeleteRow(); >- return false; >- }); >- $("#id_help").on("click",function(e){ >- e.stopPropagation(); >- alert( _("Click on the quote's id to select or deselect the quote. Multiple quotes may be selected.") ); >- }); >- }); >+ } ], >+ "columns": [ >+ { >+ "data": "quote_id", >+ "searchable": true, >+ "orderable": true >+ }, >+ { >+ "data": "source", >+ "searchable": true, >+ "orderable": true >+ }, >+ { >+ "data": "text", >+ "searchable": true, >+ "orderable": true >+ }, >+ { >+ "data": function( row, type, val, meta ) { >+ return $datetime(row.displayed_on); >+ }, >+ "searchable": true, >+ "orderable": true >+ }, >+ { >+ "data": function( row, type, val, meta ) { > >- function fnClickAddQuote(e, node) { >- if (e.charCode) { >- /* some browsers only give charCode, so this will need to be */ >- /* fixed up to handle that */ >- console.log('charCode: '+e.charCode); >- } >- if (e.keyCode == 13) { >- var quoteSource = $('#quoteSource').val(); >- var quoteText = $('#quoteText').val() >- /* If passed a quote source, add the quote to the db */ >- if (quoteSource && quoteText) { >- $.ajax({ >- url: "/cgi-bin/koha/tools/quotes/quotes_ajax.pl", >- type: "POST", >- data: { >- "source" : quoteSource, >- "text" : quoteText, >- "action" : "add", >+ var result = '<a class="btn btn-default btn-xs" href="/cgi-bin/koha/tools/quotes.pl?op=add_form&id='+encodeURIComponent(row.quote_id)+'" role="button"><i class="fa fa-pencil" aria-hidden="true"></i> '+_("Edit")+'</a>'; >+ result += '<form action="/cgi-bin/koha/tools/quotes.pl" method="post">'; >+ result += '<input type="hidden" name="id" value="'+row.quote_id.escapeHtml()+'" />'+"\n"; >+ >+ result += '<a class="btn btn-default btn-xs delete_quote" role="button" href="#" data-toggle="modal" data-target="#delete_confirm_modal" data-quote-id="'+ encodeURIComponent(row.quote_id) +'"><i class="fa fa-trash" aria-hidden="true"></i> '+_("Delete")+'</a>'; >+ >+ return result; > }, >- success: function(data){ >- var newQuote = data[0]; >- var aRow = oTable.fnUpdate( >- newQuote, >- node, >- undefined, >- false, >- false >- ); >- oTable.fnPageChange( 'last' ); >- $('.add_quote_button').attr('onclick', 'fnClickAddRow()'); // re-enable add button >- } >- }); >- } else { >- alert(_("Please supply both the text and source of the quote before saving.")); >- } >- } else if (e.keyCode == 27) { >- if (confirm(_("Are you sure you want to cancel adding this quote?"))) { >- oTable.fnDeleteRow(node); >- } else { >- return; >- } >- } >- } >- >- function fnClickAddRow() { >- $('.add_quote_button').removeAttr('onclick'); // disable add button once it has been clicked >- var aRow = oTable.fnAddData( >- [ >- 'NA', // this is hackish to fool the datatable sort routine into placing this row at the end of the list... >- '<input id="quoteSource" type="text" style="width:99%" onkeydown="fnClickAddQuote(event,this.parentNode.parentNode)"/>', >- '<input id="quoteText" type="text" style="width:99%" onkeydown="fnClickAddQuote(event,this.parentNode.parentNode)"/>', >- '0000-00-00 00:00:00', >- ], >- false >- ); >- oTable.fnPageChange( 'last' ); >- $('#quoteSource').focus(); >- } >- >- function fnClickDeleteRow() { >- var idsToDelete = oTable.$('.selected').map(function() { >- return this.id; >- }).get().join(', '); >- if (!idsToDelete) { >- alert(_("Please select a quote(s) by clicking the quote id(s) you desire to delete.")); >- } else if (confirm(_("Are you sure you wish to delete quote(s) %s?").format(idsToDelete))) { >- oTable.$('.selected').each(function(){ >- var quoteID = $(this).attr('id'); >+ "searchable": false, >+ "orderable": false >+ }, >+ ] >+ }); >+ >+ $('#quotes').on( "click", '.delete_quote', function () { >+ var quote_id = decodeURIComponent($(this).data('quote-id')); >+ >+ $("#delete_confirm_dialog").html( >+ _("You are about to delete the quote #%s.").format(quote_id) >+ ); >+ >+ $("#delete_confirm_modal_button").unbind("click").on( "click", function () { > $.ajax({ >- url: "/cgi-bin/koha/tools/quotes/quotes_ajax.pl", >- type: "POST", >- data: { >- "id" : quoteID, >- "action" : "delete", >- }, >- /* Delete the row from the datatable */ >- success: function(){ >- oTable.fnDeleteRow(this); >- oTable.fnReloadAjax(null, null, true); >- } >+ method: "DELETE", >+ url: "/api/v1/quotes/"+quote_id >+ }).success(function() { >+ $("#delete_confirm_modal").modal('hide'); >+ quotes.api().ajax.reload(function (data) { >+ if (data.recordsTotal == 0) { >+ $("#quotes").hide(); >+ } >+ $("#quote_action_result_dialog").hide(); >+ $("#quote_delete_success").html(_("Quote #%s deleted successfully.").format(quote_id)).show(); >+ }); >+ }).error(function () { >+ $("#quote_delete_error").html(_("Error deleting quote #%s. Check the logs.").format(quote_id)).show(); > }); > }); >- } else { >- return; >- } >- } >+ }); >+ >+ }); > </script> > [% END %] > >diff --git a/tools/quotes.pl b/tools/quotes.pl >index a10e5747f7..57859f14b2 100755 >--- a/tools/quotes.pl >+++ b/tools/quotes.pl >@@ -1,7 +1,5 @@ > #!/usr/bin/perl > >-# Copyright 2012 Foundations Bible College Inc. >-# > # This file is part of Koha. > # > # Koha is free software; you can redistribute it and/or modify it >@@ -20,23 +18,80 @@ > use Modern::Perl; > > use CGI qw ( -utf8 ); >-use autouse 'Data::Dumper' => qw(Dumper); >+use Try::Tiny; > > use C4::Auth; >-use C4::Koha; > use C4::Context; > use C4::Output; >+use Koha::Quotes; > >-my $cgi = CGI->new; >+my $input = CGI->new; > > my ( $template, $borrowernumber, $cookie ) = get_template_and_user( > { >- template_name => "tools/quotes.tt", >- query => $cgi, >- type => "intranet", >- flagsrequired => { tools => 'edit_quotes' }, >- debug => 1, >+ template_name => "tools/quotes.tt", >+ query => $input, >+ type => "intranet", >+ flagsrequired => { tools => 'edit_quotes' }, >+ debug => 1, > } > ); > >-output_html_with_http_headers $cgi, $cookie, $template->output; >+my $id = $input->param('id'); >+my $op = $input->param('op') || 'list'; >+my @messages; >+ >+if ( $op eq 'add_form' ) { >+ $template->param( quote => Koha::Quotes->find($id), ); >+} >+elsif ( $op eq 'add_validate' ) { >+ my @fields = qw( >+ source >+ text >+ ); >+ >+ if ($id) { >+ my $quote = Koha::Quotes->find($id); >+ for my $field (@fields) { >+ $quote->$field( scalar $input->param($field) ); >+ } >+ >+ try { >+ $quote->store; >+ push @messages, { type => 'message', code => 'success_on_update' }; >+ } >+ catch { >+ push @messages, { type => 'alert', code => 'error_on_update' }; >+ } >+ } >+ else { >+ my $quote = Koha::Quote->new( >+ { >+ id => $id, >+ ( map { $_ => scalar $input->param($_) || undef } @fields ) >+ } >+ ); >+ >+ try { >+ $quote->store; >+ push @messages, { type => 'message', code => 'success_on_insert' }; >+ } >+ catch { >+ push @messages, { type => 'alert', code => 'error_on_insert' }; >+ }; >+ } >+ $op = 'list'; >+} >+else { >+ $op = 'list'; >+} >+ >+$template->param( quotes_count => Koha::Quotes->search->count ) >+ if $op eq 'list'; >+ >+$template->param( >+ messages => \@messages, >+ op => $op, >+); >+ >+output_html_with_http_headers $input, $cookie, $template->output; >diff --git a/tools/quotes/quotes-upload_ajax.pl b/tools/quotes/quotes-upload_ajax.pl >deleted file mode 100755 >index c988e7c540..0000000000 >--- a/tools/quotes/quotes-upload_ajax.pl >+++ /dev/null >@@ -1,65 +0,0 @@ >-#!/usr/bin/perl >- >-# Copyright 2012 Foundations Bible College Inc. >-# >-# 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 <http://www.gnu.org/licenses>. >- >-use Modern::Perl; >- >-use CGI qw ( -utf8 ); >-use JSON; >-use URI::Escape; >-use autouse 'Data::Dumper' => qw(Dumper); >- >-use C4::Auth; >-use C4::Koha; >-use C4::Context; >-use C4::Output; >- >-my $cgi = CGI->new; >-my $dbh = C4::Context->dbh; >- >-my ( $status, $cookie, $sessionID ) = C4::Auth::check_api_auth( $cgi, { tools => 'edit_quotes' } ); >-unless ($status eq "ok") { >- print $cgi->header(-type => 'application/json', -status => '403 Forbidden'); >- print to_json({ auth_status => $status }); >- exit 0; >-} >- >-my $success = 'true'; >-my $quotes_tmp = uri_unescape( $cgi->param('quote' ) ); >-my $quotes = decode_json( $quotes_tmp ); >- >-my $action = $cgi->param('action'); >- >-my $sth = $dbh->prepare('INSERT INTO quotes (source, text) VALUES (?, ?);'); >- >-my $insert_count = 0; >- >-foreach my $quote (@$quotes) { >- $insert_count++ if $sth->execute($quote->[1], $quote->[2]); >- if ($sth->err) { >- warn sprintf('Database returned the following error: %s', $sth->errstr); >- $success = 'false'; >- } >-} >- >-print $cgi->header('application/json'); >- >-print to_json({ >- success => $success, >- records => $insert_count, >-}); >diff --git a/tools/quotes/quotes_ajax.pl b/tools/quotes/quotes_ajax.pl >deleted file mode 100755 >index 79fbe6fb01..0000000000 >--- a/tools/quotes/quotes_ajax.pl >+++ /dev/null >@@ -1,109 +0,0 @@ >-#!/usr/bin/perl >- >-# Copyright 2012 Foundations Bible College Inc. >-# >-# 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 <http://www.gnu.org/licenses>. >- >-use Modern::Perl; >- >-use CGI qw ( -utf8 ); >-use JSON; >-use autouse 'Data::Dumper' => qw(Dumper); >- >-use C4::Auth; >-use C4::Context; >- >-my $cgi = CGI->new; >-my $dbh = C4::Context->dbh; >-my $sort_columns = ["id", "source", "text", "timestamp"]; >- >-my ( $status, $cookie, $sessionID ) = C4::Auth::check_api_auth( $cgi, { tools => 'edit_quotes' } ); >-unless ($status eq "ok") { >- print $cgi->header(-type => 'application/json', -status => '403 Forbidden'); >- print to_json({ auth_status => $status }); >- exit 0; >-} >- >-# NOTE: This is a collection of ajax functions for use with tools/quotes.pl >- >-my $params = $cgi->Vars; # NOTE: Multivalue parameters NOT allowed!! >- >-print $cgi->header('application/json; charset=utf-8'); >- >-my $action = $params->{'action'} || 'get'; >-if ($action eq 'add') { >- my $sth = $dbh->prepare('INSERT INTO quotes (source, text) VALUES (?, ?);'); >- $sth->execute($params->{'source'}, $params->{'text'}); >- if ($sth->err) { >- warn sprintf('Database returned the following error: %s', $sth->errstr); >- exit 0; >- } >- my $new_quote_id = $dbh->{q{mysql_insertid}}; # ALERT: mysqlism here >- $sth = $dbh->prepare('SELECT * FROM quotes WHERE id = ?;'); >- $sth->execute($new_quote_id); >- print to_json($sth->fetchall_arrayref, {utf8 =>1}); >- exit 0; >-} >-elsif ($action eq 'edit') { >- my $aaData = []; >- my $editable_columns = [qw(source text)]; # pay attention to element order; these columns match the quotes table columns >- my $sth = $dbh->prepare("UPDATE quotes SET $editable_columns->[$params->{'column'}-1] = ? WHERE id = ?;"); >- $sth->execute($params->{'value'}, $params->{'id'}); >- if ($sth->err) { >- warn sprintf('Database returned the following error: %s', $sth->errstr); >- exit 1; >- } >- $sth = $dbh->prepare("SELECT $editable_columns->[$params->{'column'}-1] FROM quotes WHERE id = ?;"); >- $sth->execute($params->{'id'}); >- $aaData = $sth->fetchrow_array(); >- print Encode::encode('utf8', $aaData); >- >- exit 0; >-} >-elsif ($action eq 'delete') { >- my $sth = $dbh->prepare("DELETE FROM quotes WHERE id = ?;"); >- $sth->execute($params->{'id'}); >- if ($sth->err) { >- warn sprintf('Database returned the following error: %s', $sth->errstr); >- exit 1; >- } >- exit 0; >-} >-else { >- my $aaData = []; >- my $iTotalRecords = ''; >- my $sth = ''; >- >- $iTotalRecords = $dbh->selectrow_array('SELECT count(*) FROM quotes;'); >- $sth = $dbh->prepare("SELECT * FROM quotes;"); >- >- $sth->execute(); >- if ($sth->err) { >- warn sprintf('Database returned the following error: %s', $sth->errstr); >- exit 1; >- } >- >- $aaData = $sth->fetchall_arrayref; >- my $iTotalDisplayRecords = $iTotalRecords; # no filtering happening here >- >- >- print to_json({ >- iTotalRecords => $iTotalRecords, >- iTotalDisplayRecords=> $iTotalDisplayRecords, >- sEcho => $params->{'sEcho'}, >- aaData => $aaData, >- }, {utf8 =>1}); >-} >-- >2.20.1
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 27251
:
114435
|
114477
|
114556
|
114832
|
114885
|
114992
|
114993
|
115234
|
115235
|
115236
|
115915
|
115916
|
115917
|
115918
|
115919