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

(-)a/Koha/Quote.pm (-1 / +17 lines)
Lines 33-38 Koha::Quote - Koha Quote object class Link Here
33
33
34
=cut
34
=cut
35
35
36
=head3 to_api_mapping
37
38
This method returns the mapping for representing a Koha::Quote object
39
on the API.
40
41
=cut
42
43
sub to_api_mapping {
44
    return {
45
        id        => 'quote_id',
46
        source    => 'source',
47
        text      => 'text',
48
        timestamp => 'displayed_on',
49
    };
50
}
51
36
=head3 _type
52
=head3 _type
37
53
38
=cut
54
=cut
Lines 41-44 sub _type { Link Here
41
    return 'Quote';
57
    return 'Quote';
42
}
58
}
43
59
44
1;
60
1;
(-)a/Koha/REST/V1/Quotes.pm (+139 lines)
Line 0 Link Here
1
package Koha::REST::V1::Quotes;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use Mojo::Base 'Mojolicious::Controller';
21
22
use Koha::Quotes;
23
24
use Try::Tiny;
25
26
=head1 API
27
28
=head2 Methods
29
30
=head3 list
31
32
=cut
33
34
sub list {
35
    my $c = shift->openapi->valid_input or return;
36
37
    return try {
38
        my $quotes_set = Koha::Quotes->new;
39
        my $quotes = $c->objects->search( $quotes_set );
40
        return $c->render( status => 200, openapi => $quotes );
41
    }
42
    catch {
43
        $c->unhandled_exception($_);
44
    };
45
46
}
47
48
=head3 get
49
50
=cut
51
52
sub get {
53
    my $c = shift->openapi->valid_input or return;
54
55
    return try {
56
        my $quote = Koha::Quotes->find( $c->validation->param('quote_id') );
57
        unless ($quote) {
58
            return $c->render( status  => 404,
59
                            openapi => { error => "quote not found" } );
60
        }
61
62
        return $c->render( status => 200, openapi => $quote->to_api );
63
    }
64
    catch {
65
        $c->unhandled_exception($_);
66
    }
67
}
68
69
=head3 add
70
71
=cut
72
73
sub add {
74
    my $c = shift->openapi->valid_input or return;
75
76
    return try {
77
        my $quote = Koha::Quote->new_from_api( $c->validation->param('body') );
78
        $quote->store;
79
        $c->res->headers->location( $c->req->url->to_string . '/' . $quote->id );
80
        return $c->render(
81
            status  => 201,
82
            openapi => $quote->to_api
83
        );
84
    }
85
    catch {
86
        $c->unhandled_exception($_);
87
    };
88
}
89
90
=head3 update
91
92
=cut
93
94
sub update {
95
    my $c = shift->openapi->valid_input or return;
96
97
    my $quote = Koha::Quotes->find( $c->validation->param('quote_id') );
98
99
    if ( not defined $quote ) {
100
        return $c->render( status  => 404,
101
                           openapi => { error => "Object not found" } );
102
    }
103
104
    return try {
105
        $quote->set_from_api( $c->validation->param('body') );
106
        $quote->store();
107
        return $c->render( status => 200, openapi => $quote->to_api );
108
    }
109
    catch {
110
        $c->unhandled_exception($_);
111
    };
112
}
113
114
=head3 delete
115
116
=cut
117
118
sub delete {
119
    my $c = shift->openapi->valid_input or return;
120
121
    my $quote = Koha::Quotes->find( $c->validation->param('quote_id') );
122
    if ( not defined $quote ) {
123
        return $c->render( status  => 404,
124
                           openapi => { error => "Object not found" } );
125
    }
126
127
    return try {
128
        $quote->delete;
129
        return $c->render(
130
            status  => 204,
131
            openapi => q{}
132
        );
133
    }
134
    catch {
135
        $c->unhandled_exception($_);
136
    };
137
}
138
139
1;
(-)a/api/v1/swagger/definitions.json (+3 lines)
Lines 65-70 Link Here
65
  "patron_balance": {
65
  "patron_balance": {
66
    "$ref": "definitions/patron_balance.json"
66
    "$ref": "definitions/patron_balance.json"
67
  },
67
  },
68
  "quote": {
69
    "$ref": "definitions/quote.json"
70
  },
68
  "allows_renewal": {
71
  "allows_renewal": {
69
    "$ref": "definitions/allows_renewal.json"
72
    "$ref": "definitions/allows_renewal.json"
70
  },
73
  },
(-)a/api/v1/swagger/definitions/quote.json (+22 lines)
Line 0 Link Here
1
{
2
  "type": "object",
3
  "properties": {
4
    "quote_id": {
5
      "$ref": "../x-primitives.json#/quote_id"
6
    },
7
    "source": {
8
      "description": "source of the quote",
9
      "type": "string"
10
    },
11
    "text": {
12
      "description": "text",
13
      "type": ["string", "null"]
14
    },
15
    "displayed_on": {
16
      "description": "Last display date",
17
      "type": ["string", "null"]
18
    }
19
  },
20
  "additionalProperties": false,
21
  "required": ["quote_id", "source", "text"]
22
}
(-)a/api/v1/swagger/parameters.json (+3 lines)
Lines 32-37 Link Here
32
  "order_id_pp": {
32
  "order_id_pp": {
33
    "$ref": "parameters/order.json#/order_id_pp"
33
    "$ref": "parameters/order.json#/order_id_pp"
34
  },
34
  },
35
  "quote_id_pp": {
36
    "$ref": "parameters/quote.json#/quote_id_pp"
37
  },
35
  "smtp_server_id_pp": {
38
  "smtp_server_id_pp": {
36
    "$ref": "parameters/smtp_server.json#/smtp_server_id_pp"
39
    "$ref": "parameters/smtp_server.json#/smtp_server_id_pp"
37
  },
40
  },
(-)a/api/v1/swagger/parameters/quote.json (+9 lines)
Line 0 Link Here
1
{
2
    "quote_id_pp": {
3
      "name": "quote_id",
4
      "in": "path",
5
      "description": "Quote internal identifier",
6
      "required": true,
7
      "type": "integer"
8
    }
9
}
(-)a/api/v1/swagger/paths.json (+6 lines)
Lines 134-139 Link Here
134
  "/public/patrons/{patron_id}/guarantors/can_see_checkouts": {
134
  "/public/patrons/{patron_id}/guarantors/can_see_checkouts": {
135
    "$ref": "paths/public_patrons.json#/~1public~1patrons~1{patron_id}~1guarantors~1can_see_checkouts"
135
    "$ref": "paths/public_patrons.json#/~1public~1patrons~1{patron_id}~1guarantors~1can_see_checkouts"
136
  },
136
  },
137
  "/quotes": {
138
    "$ref": "paths/quotes.json#/~1quotes"
139
  },
140
  "/quotes/{quote_id}": {
141
    "$ref": "paths/quotes.json#/~1quotes~1{quote_id}"
142
  },
137
  "/return_claims": {
143
  "/return_claims": {
138
    "$ref": "paths/return_claims.json#/~1return_claims"
144
    "$ref": "paths/return_claims.json#/~1return_claims"
139
  },
145
  },
(-)a/api/v1/swagger/paths/quotes.json (+327 lines)
Line 0 Link Here
1
{
2
  "/quotes": {
3
    "get": {
4
      "x-mojo-to": "Quotes#list",
5
      "operationId": "listQuotes",
6
      "tags": [
7
        "quotes"
8
      ],
9
      "produces": [
10
        "application/json"
11
      ],
12
      "parameters": [
13
        {
14
          "name": "quote_id",
15
          "in": "query",
16
          "description": "Case insensitive search on quote id",
17
          "required": false,
18
          "type": "string"
19
        },
20
        {
21
          "name": "source",
22
          "in": "query",
23
          "description": "Case insensitive search on source",
24
          "required": false,
25
          "type": "string"
26
        },
27
        {
28
          "name": "text",
29
          "in": "query",
30
          "description": "Case insensitive search on text",
31
          "required": false,
32
          "type": "string"
33
        },
34
        {
35
          "name": "displayed_on",
36
          "in": "query",
37
          "description": "Case Insensative search on last displayed date",
38
          "required": false,
39
          "type": "string"
40
        },
41
        {
42
          "$ref": "../parameters.json#/match"
43
        },
44
        {
45
          "$ref": "../parameters.json#/order_by"
46
        },
47
        {
48
          "$ref": "../parameters.json#/page"
49
        },
50
        {
51
          "$ref": "../parameters.json#/per_page"
52
        },
53
        {
54
          "$ref": "../parameters.json#/q_param"
55
        },
56
        {
57
          "$ref": "../parameters.json#/q_body"
58
        },
59
        {
60
          "$ref": "../parameters.json#/q_header"
61
        }
62
      ],
63
      "responses": {
64
        "200": {
65
          "description": "A list of quotes",
66
          "schema": {
67
            "type": "array",
68
            "items": {
69
              "$ref": "../definitions.json#/quote"
70
            }
71
          }
72
        },
73
        "403": {
74
          "description": "Access forbidden",
75
          "schema": {
76
            "$ref": "../definitions.json#/error"
77
          }
78
        },
79
        "500": {
80
          "description": "Internal error",
81
          "schema": {
82
            "$ref": "../definitions.json#/error"
83
          }
84
        },
85
        "503": {
86
          "description": "Under maintenance",
87
          "schema": {
88
            "$ref": "../definitions.json#/error"
89
          }
90
        }
91
      },
92
      "x-koha-authorization": {
93
        "permissions": {
94
          "catalogue": "1"
95
        }
96
      }
97
    },
98
    "post": {
99
      "x-mojo-to": "Quotes#add",
100
      "operationId": "addQuote",
101
      "tags": [
102
        "quotes"
103
      ],
104
      "parameters": [
105
        {
106
          "name": "body",
107
          "in": "body",
108
          "description": "A JSON object containing informations about the new quote",
109
          "required": true,
110
          "schema": {
111
            "$ref": "../definitions.json#/quote"
112
          }
113
        }
114
      ],
115
      "produces": [
116
        "application/json"
117
      ],
118
      "responses": {
119
        "201": {
120
          "description": "Quote added",
121
          "schema": {
122
            "$ref": "../definitions.json#/quote"
123
          }
124
        },
125
        "401": {
126
          "description": "Authentication required",
127
          "schema": {
128
            "$ref": "../definitions.json#/error"
129
          }
130
        },
131
        "403": {
132
          "description": "Access forbidden",
133
          "schema": {
134
            "$ref": "../definitions.json#/error"
135
          }
136
        },
137
        "500": {
138
          "description": "Internal error",
139
          "schema": {
140
            "$ref": "../definitions.json#/error"
141
          }
142
        },
143
        "503": {
144
          "description": "Under maintenance",
145
          "schema": {
146
            "$ref": "../definitions.json#/error"
147
          }
148
        }
149
      },
150
      "x-koha-authorization": {
151
        "permissions": {
152
          "tools": "edit_quotes"
153
        }
154
      }
155
    }
156
  },
157
  "/quotes/{quote_id}": {
158
    "get": {
159
      "x-mojo-to": "Quotes#get",
160
      "operationId": "getQuote",
161
      "tags": [
162
        "quotes"
163
      ],
164
      "parameters": [
165
        {
166
          "$ref": "../parameters.json#/quote_id_pp"
167
        }
168
      ],
169
      "produces": [
170
        "application/json"
171
      ],
172
      "responses": {
173
        "200": {
174
          "description": "A Quote",
175
          "schema": {
176
            "$ref": "../definitions.json#/quote"
177
          }
178
        },
179
        "404": {
180
          "description": "Quote not found",
181
          "schema": {
182
            "$ref": "../definitions.json#/error"
183
          }
184
        },
185
        "500": {
186
          "description": "Internal error",
187
          "schema": {
188
            "$ref": "../definitions.json#/error"
189
          }
190
        },
191
        "503": {
192
          "description": "Under maintenance",
193
          "schema": {
194
            "$ref": "../definitions.json#/error"
195
          }
196
        }
197
      },
198
      "x-koha-authorization": {
199
        "permissions": {
200
          "catalogue": "1"
201
        }
202
      }
203
    },
204
    "put": {
205
      "x-mojo-to": "Quotes#update",
206
      "operationId": "updateQuote",
207
      "tags": [
208
        "quotes"
209
      ],
210
      "parameters": [
211
        {
212
          "$ref": "../parameters.json#/quote_id_pp"
213
        },
214
        {
215
          "name": "body",
216
          "in": "body",
217
          "description": "a quote object",
218
          "required": true,
219
          "schema": {
220
            "$ref": "../definitions.json#/quote"
221
          }
222
        }
223
      ],
224
      "produces": [
225
        "application/json"
226
      ],
227
      "responses": {
228
        "200": {
229
          "description": "A quote",
230
          "schema": {
231
            "$ref": "../definitions.json#/quote"
232
          }
233
        },
234
        "401": {
235
          "description": "Authentication required",
236
          "schema": {
237
            "$ref": "../definitions.json#/error"
238
          }
239
        },
240
        "403": {
241
          "description": "Access forbidden",
242
          "schema": {
243
            "$ref": "../definitions.json#/error"
244
          }
245
        },
246
        "404": {
247
          "description": "Quote not found",
248
          "schema": {
249
            "$ref": "../definitions.json#/error"
250
          }
251
        },
252
        "500": {
253
          "description": "Internal error",
254
          "schema": {
255
            "$ref": "../definitions.json#/error"
256
          }
257
        },
258
        "503": {
259
          "description": "Under maintenance",
260
          "schema": {
261
            "$ref": "../definitions.json#/error"
262
          }
263
        }
264
      },
265
      "x-koha-authorization": {
266
        "permissions": {
267
          "tools": "edit_quotes"
268
        }
269
      }
270
    },
271
    "delete": {
272
      "x-mojo-to": "Quotes#delete",
273
      "operationId": "deleteQuote",
274
      "tags": [
275
        "quotes"
276
      ],
277
      "parameters": [
278
        {
279
          "$ref": "../parameters.json#/quote_id_pp"
280
        }
281
      ],
282
      "produces": [
283
        "application/json"
284
      ],
285
      "responses": {
286
        "204": {
287
          "description": "Quote deleted"
288
        },
289
        "401": {
290
          "description": "Authentication required",
291
          "schema": {
292
            "$ref": "../definitions.json#/error"
293
          }
294
        },
295
        "403": {
296
          "description": "Access forbidden",
297
          "schema": {
298
            "$ref": "../definitions.json#/error"
299
          }
300
        },
301
        "404": {
302
          "description": "Quote not found",
303
          "schema": {
304
            "$ref": "../definitions.json#/error"
305
          }
306
        },
307
        "500": {
308
          "description": "Internal error",
309
          "schema": {
310
            "$ref": "../definitions.json#/error"
311
          }
312
        },
313
        "503": {
314
          "description": "Under maintenance",
315
          "schema": {
316
            "$ref": "../definitions.json#/error"
317
          }
318
        }
319
      },
320
      "x-koha-authorization": {
321
        "permissions": {
322
          "tools": "edit_quotes"
323
        }
324
      }
325
    }
326
  }
327
}
(-)a/api/v1/swagger/x-primitives.json (-1 / +5 lines)
Lines 52-57 Link Here
52
    "type": "integer",
52
    "type": "integer",
53
    "description": "internally assigned fund identifier",
53
    "description": "internally assigned fund identifier",
54
    "readOnly": true
54
    "readOnly": true
55
  },
56
  "quote_id": {
57
    "type": "integer",
58
    "description": "internally assigned quote identifier",
59
    "readOnly": true
55
  }
60
  }
56
57
}
61
}
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/quotes-toolbar.inc (-5 lines)
Lines 1-5 Link Here
1
<div id="toolbar" class="btn-toolbar">
2
        <div class="btn-group"><a class="btn btn-default" id="add_quote" href="#"><i class="fa fa-plus"></i> Add quote</a></div>
3
        <div class="btn-group"><a class="btn btn-default" id="delete_quote" href="#"><i class="fa fa-trash"></i> Delete quote(s)</a></div>
4
        <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>
5
</div>
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/quotes-upload-toolbar.inc (-5 lines)
Lines 1-5 Link Here
1
<div id="toolbar" class="btn-toolbar" style="visibility: hidden; position: absolute">
2
        <div class="btn-group"><a class="btn btn-default" id="save_quotes" href="#"><i class="fa fa-save"></i> Save quotes</a></div>
3
        <div class="btn-group"><a class="btn btn-default" id="delete_quote" href="#"><i class="fa fa-trash"></i> Delete quote(s)</a></div>
4
        <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>
5
</div>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tt (-1 / +1 lines)
Lines 37-43 Link Here
37
                [% END %]
37
                [% END %]
38
                [% IF ( daily_quote ) %]
38
                [% IF ( daily_quote ) %]
39
                    <div id="area-news">
39
                    <div id="area-news">
40
                        <h3>Quote of the Day</h3>
40
                        <h3>Quote of the day</h3>
41
                        <div class="newsitem">
41
                        <div class="newsitem">
42
                            <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>
42
                            <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>
43
                        </div>
43
                        </div>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/quotes-upload.tt (-39 / +45 lines)
Lines 18-26 Link Here
18
    <div class="row">
18
    <div class="row">
19
        <div class="col-sm-10 col-sm-push-2">
19
        <div class="col-sm-10 col-sm-push-2">
20
            <main>
20
            <main>
21
                <div id="toolbar" class="btn-toolbar" style="visibility: hidden; position: absolute">
22
                    <div class="btn-group"><a class="btn btn-default" id="save_quotes" href="#"><i class="fa fa-save"></i> Save quotes</a></div>
23
                    <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>
24
                </div>
21
25
22
                [% INCLUDE 'quotes-upload-toolbar.inc' %]
23
                <h2>Quote uploader</h2>
26
                <h2>Quote uploader</h2>
27
28
                <div id="messages" style="display: none;">
29
                    <div class="import_success dialog message" style="display: none;"></div>
30
                    <div class="import_errors dialog alert" style="display: none;"></div>
31
                </div>
32
24
                <div id="instructions">
33
                <div id="instructions">
25
                <fieldset id="file_uploader_help" class="rows">
34
                <fieldset id="file_uploader_help" class="rows">
26
                    <legend>Instructions</legend>
35
                    <legend>Instructions</legend>
Lines 34-45 Link Here
34
                    <div id="file_editor_inst">
43
                    <div id="file_editor_inst">
35
                        <ul>
44
                        <ul>
36
                        <li>Click on any field to edit the contents; Press the &lt;Enter&gt; key to save edit.</li>
45
                        <li>Click on any field to edit the contents; Press the &lt;Enter&gt; key to save edit.</li>
37
                        <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>
38
                        <li>Click the 'Save Quotes' button in the toolbar to save the entire batch of quotes.</li>
46
                        <li>Click the 'Save Quotes' button in the toolbar to save the entire batch of quotes.</li>
39
                        </ul>
47
                        </ul>
40
                    </div>
48
                    </div>
41
                </fieldset>
49
                </fieldset>
42
                </div>
50
                </div>
51
43
                <fieldset id="file_uploader" class="rows">
52
                <fieldset id="file_uploader" class="rows">
44
                    <legend>Upload quotes</legend>
53
                    <legend>Upload quotes</legend>
45
                    <div id="file_upload">
54
                    <div id="file_upload">
Lines 232-238 Link Here
232
            $('#file_uploader').css("top","-150px");
241
            $('#file_uploader').css("top","-150px");
233
            $('#quotes_editor').css("visibility","visible");
242
            $('#quotes_editor').css("visibility","visible");
234
            $("#save_quotes").on("click", yuiGetData);
243
            $("#save_quotes").on("click", yuiGetData);
235
            $("#delete_quote").on("click", fnClickDeleteRow);
236
244
237
            oTable = $('#quotes_editor').dataTable( {
245
            oTable = $('#quotes_editor').dataTable( {
238
                "bAutoWidth"        : false,
246
                "bAutoWidth"        : false,
Lines 262-269 Link Here
262
                    /* do foo on various cells in the current row */
270
                    /* do foo on various cells in the current row */
263
                    var quoteNum = $('td', nRow)[0].innerHTML;
271
                    var quoteNum = $('td', nRow)[0].innerHTML;
264
                    $(nRow).attr("id", quoteNum); /* set row ids to quote number */
272
                    $(nRow).attr("id", quoteNum); /* set row ids to quote number */
265
                    $('td:eq(0)', nRow).click(function() {$(this.parentNode).toggleClass('selected',this.clicked);}); /* add row selectors */
266
                    $('td:eq(0)', nRow).attr("title", _("Click ID to select/deselect quote"));
267
                    /* apply no_edit id to noEditFields */
273
                    /* apply no_edit id to noEditFields */
268
                    noEditFields = [0]; /* number */
274
                    noEditFields = [0]; /* number */
269
                    for (i=0; i<noEditFields.length; i++) {
275
                    for (i=0; i<noEditFields.length; i++) {
Lines 335-378 Link Here
335
341
336
        $('#file_upload').one('change', fnHandleFileSelect);
342
        $('#file_upload').one('change', fnHandleFileSelect);
337
343
338
        });
344
        var MSG_IMPORT_SUCCESS = _("%s quotes imported successfully");
339
345
        var MSG_IMPORT_ERROR   = _("%s quotes have not been imported. An error occurred");
340
        function fnGetData(element) {
346
        function fnGetData(element) {
341
            var jqXHR = $.ajax({
347
            var lines = oTable.fnGetData();
342
                url         : "/cgi-bin/koha/tools/quotes/quotes-upload_ajax.pl",
348
            $(lines).each(function(line){
343
                type        : "POST",
349
                var data = {source: this[1], text: this[2]};
344
                contentType : "application/x-www-form-urlencoded", // we must claim this mimetype or CGI will not decode the URL encoding
350
                var success = 0; var error = 0;
345
                dataType    : "json",
351
                $.ajax({
346
                data        : {
352
                    url      : "/api/v1/quotes",
347
                                "quote"     : encodeURI ( JSON.stringify(oTable.fnGetData()) ),
353
                    method   : "POST",
348
                                "action"    : "add",
354
                    data     : JSON.stringify(data),
349
                              },
355
                    dataType : "application/json",
350
                success     : function(){
356
                    success  : function(data) {
351
                    var response = JSON.parse(jqXHR.responseText);
357
                        $("#messages").show();
352
                    if (response.success) {
358
                        var import_success = $("#messages .import_success");
353
                        alert(_("%s quotes saved.").format(response.records));
359
                        import_success.show();
354
                        window.location.reload(true);   // is this the best route?
360
                        import_success.data("nb")
355
                    } else {
361
                        nb_success = import_success.data("nb") || 0;
356
                        alert(_("%s quotes saved, but an error has occurred. Please ask your administrator to check the server log for more details.").format(response.records));
362
                        nb_success++;
357
                        window.location.reload(true);   // is this the best route?
363
                        $("#messages .import_success").text(MSG_IMPORT_SUCCESS.format(nb_success));
358
                    }
364
                        import_success.data("nb", nb_success);
359
                  },
365
                    },
366
                    error    : function(xhr) {
367
                        if (xhr.status==201) { this.success(null, "Created", xhr); return; }
368
                        $("#messages").show();
369
                        var import_error = $("#messages .import_error");
370
                        import_error.show();
371
                        import_error.data("nb")
372
                        nb_error = import_error.data("nb") || 0;
373
                        nb_error++;
374
                        $("#messages .import_error").text(MSG_IMPORT_ERROR.format(nb_error));
375
                        import_error.data("nb", nb_error);
376
                    },
377
                });
360
            });
378
            });
361
        }
379
        }
362
380
363
        function fnClickDeleteRow() {
381
        }); // $(document).ready
364
            var idsToDelete = oTable.$('.selected').map(function() {
365
                  return this.id;
366
            }).get().join(', ');
367
            if (!idsToDelete) {
368
                alert(_("Please select a quote(s) by clicking the quote id(s) you desire to delete."));
369
            }
370
            else if (confirm(_("Are you sure you wish to delete quote(s) %s?").format(idsToDelete))) {
371
                oTable.$('.selected').each(function(){
372
                    oTable.fnDeleteRow(this);
373
                });
374
            }
375
        }
376
    </script>
382
    </script>
377
[% END %]
383
[% END %]
378
384
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/quotes.tt (-188 / +190 lines)
Lines 1-10 Link Here
1
[% USE raw %]
1
[% USE raw %]
2
[% USE Asset %]
2
[% USE Asset %]
3
[% SET footerjs = 1 %]
3
[% SET footerjs = 1 %]
4
    [% INCLUDE 'doc-head-open.inc' %]
4
[% INCLUDE 'doc-head-open.inc' %]
5
    <title>Koha &rsaquo; Tools &rsaquo; Quote editor</title>
5
<title>Koha &rsaquo; Tools &rsaquo; Quote editor</title>
6
    [% INCLUDE 'doc-head-close.inc' %]
6
[% INCLUDE 'doc-head-close.inc' %]
7
    [% Asset.css("css/quotes.css") | $raw %]
7
[% Asset.css("css/quotes.css") | $raw %]
8
</head>
8
</head>
9
9
10
<body id="tools_quotes" class="tools">
10
<body id="tools_quotes" class="tools">
Lines 18-60 Link Here
18
        <div class="col-sm-10 col-sm-push-2">
18
        <div class="col-sm-10 col-sm-push-2">
19
            <main>
19
            <main>
20
20
21
                [% INCLUDE 'quotes-toolbar.inc' %]
21
[% FOREACH m IN messages %]
22
                <h2>Quote editor</h2>
22
    <div class="dialog [% m.type | html %]" id="quote_action_result_dialog">
23
                <div id="instructions">
23
        [% SWITCH m.code %]
24
                <fieldset id="quote_editor_help" class="rows">
24
        [% CASE 'error_on_update' %]
25
                    <legend>Instructions</legend>
25
            An error occurred when updating this quote. Perhaps it already exists.
26
                    <div id="quote_editor_inst">
26
        [% CASE 'error_on_insert' %]
27
                        <ul>
27
            An error occurred when adding this quote.
28
                        <li>Click on the 'Add quote' button to add a single quote; Press the &lt;Enter&gt; key to save the quote.<br />
28
        [% CASE 'success_on_update' %]
29
                            <strong>Note: </strong>Both the 'source' and 'text' fields must have content in order for the quote to be saved.</li>
29
            Quote updated successfully.
30
                        <li>Click on any field to edit the contents; Press the &lt;Enter&gt; key to save edit.</li>
30
        [% CASE 'success_on_insert' %]
31
                        <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>
31
            Quote added successfully.
32
                        <li>Click the 'Import quotes' button in the toolbar to import a CSV file of quotes.</li>
32
        [% CASE %]
33
                        </ul>
33
            [% m.code | html %]
34
                    </div>
34
        [% END %]
35
                </fieldset>
35
    </div>
36
[% END %]
37
38
    <div class="dialog message" id="quote_delete_success" style="display: none;"></div>
39
    <div class="dialog alert"   id="quote_delete_error"   style="display: none;"></div>
40
41
[% IF op == 'list' %]
42
    <div id="toolbar" class="btn-toolbar">
43
        <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>
44
        <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>
45
    </div>
46
[% END %]
47
48
[% IF op == 'add_form' %]
49
    <h3>[% IF quote %]Modify quote[% ELSE %]New quote[% END %]</h3>
50
    <form action="/cgi-bin/koha/tools/quotes.pl" id="Aform" name="Aform" class="validated" method="post">
51
        <fieldset class="rows">
52
            <input type="hidden" name="op" value="add_validate" />
53
            <ol>
54
                <li>
55
                    <label for="text" class="required">Source: </label>
56
                    <input type="text" name="source" id="source" value="[% quote.source | html %]" class="required" required="required" />
57
                    <span class="required">Required</span>
58
                </li>
59
                <li>
60
                    <label for="text" class="required">Text: </label>
61
                    <textarea name="text" id="text" class="required" required="required" />[% quote.text | html %]</textarea>
62
                    <span class="required">Required</span>
63
                </li>
64
            </ol>
65
        </fieldset>
66
        <fieldset class="action">
67
            <input type="hidden" name="id" value="[% quote.id %]" />
68
            <input type="submit" value="Submit" />
69
            <a class="cancel" href="/cgi-bin/koha/tools/quotes.pl">Cancel</a>
70
        </fieldset>
71
    </form>
72
[% END %]
73
74
[% IF op == 'delete_confirm' %]
75
    <div class="dialog alert">
76
        <form action="/cgi-bin/koha/tools/quotes.pl" method="post">
77
            <h3>Are you sure you want to delete the following quote?</h3>
78
            [% quote.source | html %] - [% quote.text | html %]
79
            <input type="hidden" name="op" value="delete_confirmed" />
80
            <input type="hidden" name="id" value="[% quote.id | html %]" />
81
            <button type="submit" class="approve"><i class="fa fa-fw fa-check"></i> Yes, delete</button>
82
        </form>
83
        <form action="/cgi-bin/koha/tools/quotes.pl" method="get">
84
            <button type="submit" class="deny"><i class="fa fa-fw fa-remove"></i> No, do not delete</button>
85
        </form>
86
    </div>
87
[% END %]
88
89
[% IF op == 'list' %]
90
    <h3>Quotes</h3>
91
    [% IF quotes_count > 0 %]
92
        <table id="quotes">
93
            <thead>
94
                <tr>
95
                    <th>ID</th>
96
                    <th>Source</th>
97
                    <th>Text</th>
98
                    <th>Last display</th>
99
                    <th data-class-name="actions">Actions</th>
100
                </tr>
101
            </thead>
102
        </table>
103
    [% ELSE %]
104
        <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>
105
    [% END %]
106
107
    <div id="delete_confirm_modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="delete_confirm_modal_label" aria-hidden="true">
108
        <div class="modal-dialog">
109
            <div class="modal-content">
110
                <div class="modal-header">
111
                    <button type="button" class="closebtn" data-dismiss="modal" aria-hidden="true">×</button>
112
                    <h3 id="delete_confirm_modal_label">Delete quote</h3>
36
                </div>
113
                </div>
37
                <table id="quotes_editor">
114
                <div class="modal-body">
38
                <thead>
115
                    <div id="delete_confirm_dialog"></div>
39
                    <tr>
116
                </div>
40
                        <th><span style="cursor: help" id="id_help">ID</span></th>
117
                <div class="modal-footer">
41
                        <th>Source</th>
118
                    <a href="#" class="btn btn-default" id="delete_confirm_modal_button" role="button" data-toggle="modal">Delete</a>
42
                        <th>Text</th>
119
                    <button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>
43
                        <th>Last displayed</th>
120
                </div>
44
                    </tr>
121
            </div> <!-- /.modal-content -->
45
                </thead>
122
        </div> <!-- /.modal-dialog -->
46
                <tbody>
123
    </div> <!-- #delete_confirm_modal -->
47
                    <!-- tbody content is generated by DataTables -->
124
[% END %]
48
                    <tr>
49
                        <td></td>
50
                        <td></td>
51
                        <td>Loading data...</td>
52
                        <td></td>
53
                    </tr>
54
                </tbody>
55
                </table>
56
                <fieldset id="footer" class="action">
57
                </fieldset>
58
125
59
            </main>
126
            </main>
60
        </div> <!-- /.col-sm-10.col-sm-push-2 -->
127
        </div> <!-- /.col-sm-10.col-sm-push-2 -->
Lines 68-231 Link Here
68
135
69
[% MACRO jsinclude BLOCK %]
136
[% MACRO jsinclude BLOCK %]
70
    [% Asset.js("js/tools-menu.js") | $raw %]
137
    [% Asset.js("js/tools-menu.js") | $raw %]
138
    [% INCLUDE 'js-date-format.inc' %]
71
    [% INCLUDE 'datatables.inc' %]
139
    [% INCLUDE 'datatables.inc' %]
72
    [% Asset.js("lib/jquery/plugins/dataTables.fnReloadAjax.js") | $raw %]
140
73
    [% Asset.js("lib/jquery/plugins/jquery.jeditable.mini.js") | $raw %]
74
    <script>
141
    <script>
75
        var oTable; /* oTable needs to be global */
76
        var sEmptyTable = _("No quotes available. Please use the 'Add quote' button to add a quote."); /* override the default message in datatables.inc */
77
        $(document).ready(function() {
142
        $(document).ready(function() {
78
            /* NOTE: This is an ajax-source datatable and *not* a server-side sourced datatable. */
143
79
            /* See the datatable docs if you don't understand this difference. */
144
            var quotes_url = '/api/v1/quotes';
80
            oTable = $("#quotes_editor").dataTable({
145
            var quotes = $("#quotes").api({
81
                "bAutoWidth"        : false,
146
                "ajax": {
82
                "bProcessing"       : true,
147
                    "url": quotes_url
83
                "bPaginate"         : true,
84
                "sPaginationType"   : "full_numbers",
85
                "sDom": '<"top pager"iflp>rt<"bottom pager"flp><"clear">',
86
                "sAjaxSource"       : "/cgi-bin/koha/tools/quotes/quotes_ajax.pl",
87
                "aoColumns"         : [
88
                                        { "sWidth": "3%"  },
89
                                        { "sWidth": "11%" },
90
                                        { "sWidth": "75%" },
91
                                        { "sWidth": "11%" },
92
                                      ],
93
               "oLanguage": dataTablesDefaults.oLanguage,
94
               "fnPreDrawCallback": function(oSettings) {
95
                    return true;
96
                },
148
                },
97
                "fnRowCallback": function( nRow, aData, iDisplayIndex ) {
149
                '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>',
98
                    /* do foo on the current row and its child nodes */
150
                "columnDefs": [ {
99
                    var noEditFields = [];
151
                    "targets": [0,1,2,3],
100
                    var quoteID = $('td', nRow)[0].innerHTML;
152
                    "render": function (data, type, row, meta) {
101
                    $(nRow).attr("id", quoteID); /* set row ids to quote id */
153
                        if ( type == 'display' ) {
102
                    $('td:eq(0)', nRow).click(function() {$(this.parentNode).toggleClass('selected',this.clicked);}); /* add row selectors */
154
                            if ( data != null ) {
103
                    $('td:eq(0)', nRow).attr("title", _("Click ID to select/deselect quote"));
155
                                return data.escapeHtml();
104
                    $('td', nRow).attr("id",quoteID); /* FIXME: this is a bit of a hack */
156
                            }
105
                    if (isNaN(quoteID)) {
157
                            else {
106
                        noEditFields = [0,1,2,3]; /* all fields when adding a quote */
158
                                return "";
107
                    } else {
159
                            }
108
                        noEditFields = [0,3]; /* id, timestamp */
160
                        }
109
                    }
161
                        return data;
110
                    /* apply no_edit id to noEditFields */
111
                    for (i=0; i<noEditFields.length; i++) {
112
                        $('td', nRow)[noEditFields[i]].setAttribute("id","no_edit");
113
                    }
162
                    }
114
                    return nRow;
163
                } ],
115
                },
164
                "columns": [
116
               "fnDrawCallback": function(oSettings) {
165
                    {
117
                    /* Apply the jEditable handlers to the table on all fields w/o the no_edit id */
166
                        "data": "quote_id",
118
                    $('#quotes_editor tbody td[id!="no_edit"]').editable( "/cgi-bin/koha/tools/quotes/quotes_ajax.pl", {
167
                        "searchable": true,
119
                        "submitdata"    : function ( value, settings ) {
168
                        "orderable": true
120
                                              return {
169
                    },
121
                                                  "column"        : oTable.fnGetPosition( this )[2],
170
                    {
122
                                                  "action"        : "edit",
171
                        "data": "source",
123
                                              };
172
                        "searchable": true,
124
                                          },
173
                        "orderable": true
125
                        "placeholder"   : "Saving data...",
174
                    },
126
                    });
175
                    {
127
               },
176
                        "data": "text",
128
            });
177
                        "searchable": true,
129
            $("#add_quote").click(function(){
178
                        "orderable": true
130
                fnClickAddRow();
179
                    },
131
                return false;
180
                    {
132
            });
181
                        "data": function( row, type, val, meta ) {
133
            $("#delete_quote").click(function(){
182
                            return $datetime(row.displayed_on);
134
                fnClickDeleteRow();
183
                        },
135
                return false;
184
                        "searchable": true,
136
            });
185
                        "orderable": true
137
            $("#id_help").on("click",function(e){
186
                    },
138
                e.stopPropagation();
187
                    {
139
                alert( _("Click on the quote's id to select or deselect the quote. Multiple quotes may be selected.") );
188
                        "data": function( row, type, val, meta ) {
140
            });
141
        });
142
189
143
        function fnClickAddQuote(e, node) {
190
                            var result = '<a class="btn btn-default btn-xs" href="/cgi-bin/koha/tools/quotes.pl?op=add_form&amp;id='+encodeURIComponent(row.quote_id)+'" role="button"><i class="fa fa-pencil" aria-hidden="true"></i> '+_("Edit")+'</a>';
144
            if (e.charCode) {
191
                            result += '<form action="/cgi-bin/koha/tools/quotes.pl" method="post">';
145
                /* some browsers only give charCode, so this will need to be */
192
                            result += '<input type="hidden" name="id" value="'+row.quote_id.escapeHtml()+'" />'+"\n";
146
                /* fixed up to handle that */
193
147
                console.log('charCode: '+e.charCode);
194
                            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>';
148
            }
195
149
            if (e.keyCode == 13) {
196
                            return result;
150
                var quoteSource = $('#quoteSource').val();
151
                var quoteText = $('#quoteText').val()
152
                /* If passed a quote source, add the quote to the db */
153
                if (quoteSource && quoteText) {
154
                    $.ajax({
155
                        url: "/cgi-bin/koha/tools/quotes/quotes_ajax.pl",
156
                        type: "POST",
157
                        data: {
158
                            "source"    : quoteSource,
159
                            "text"      : quoteText,
160
                            "action"    : "add",
161
                        },
197
                        },
162
                        success: function(data){
198
                        "searchable": false,
163
                            var newQuote = data[0];
199
                        "orderable": false
164
                            var aRow = oTable.fnUpdate(
200
                    },
165
                                newQuote,
201
                ]
166
                                node,
202
            });
167
                                undefined,
203
168
                                false,
204
            $('#quotes').on( "click", '.delete_quote', function () {
169
                                false
205
                var quote_id   = decodeURIComponent($(this).data('quote-id'));
170
                            );
206
171
                            oTable.fnPageChange( 'last' );
207
                $("#delete_confirm_dialog").html(
172
                            $('.add_quote_button').attr('onclick', 'fnClickAddRow()'); // re-enable add button
208
                    _("You are about to delete the quote #%s.").format(quote_id)
173
                        }
209
                );
174
                    });
210
175
                } else {
211
                $("#delete_confirm_modal_button").unbind("click").on( "click", function () {
176
                    alert(_("Please supply both the text and source of the quote before saving."));
177
                }
178
            } else if (e.keyCode == 27) {
179
                if (confirm(_("Are you sure you want to cancel adding this quote?"))) {
180
                    oTable.fnDeleteRow(node);
181
                } else {
182
                    return;
183
                }
184
            }
185
        }
186
187
        function fnClickAddRow() {
188
            $('.add_quote_button').removeAttr('onclick'); // disable add button once it has been clicked
189
            var aRow = oTable.fnAddData(
190
                [
191
                    'NA', // this is hackish to fool the datatable sort routine into placing this row at the end of the list...
192
                    '<input id="quoteSource" type="text" style="width:99%" onkeydown="fnClickAddQuote(event,this.parentNode.parentNode)"/>',
193
                    '<input id="quoteText" type="text" style="width:99%" onkeydown="fnClickAddQuote(event,this.parentNode.parentNode)"/>',
194
                    '0000-00-00 00:00:00',
195
                ],
196
                false
197
            );
198
            oTable.fnPageChange( 'last' );
199
            $('#quoteSource').focus();
200
        }
201
202
        function fnClickDeleteRow() {
203
            var idsToDelete = oTable.$('.selected').map(function() {
204
                  return this.id;
205
            }).get().join(', ');
206
            if (!idsToDelete) {
207
                alert(_("Please select a quote(s) by clicking the quote id(s) you desire to delete."));
208
            } else if (confirm(_("Are you sure you wish to delete quote(s) %s?").format(idsToDelete))) {
209
                oTable.$('.selected').each(function(){
210
                    var quoteID = $(this).attr('id');
211
                    $.ajax({
212
                    $.ajax({
212
                        url: "/cgi-bin/koha/tools/quotes/quotes_ajax.pl",
213
                        method: "DELETE",
213
                        type: "POST",
214
                        url: "/api/v1/quotes/"+quote_id
214
                        data: {
215
                    }).success(function() {
215
                                "id"        : quoteID,
216
                        $("#delete_confirm_modal").modal('hide');
216
                                "action"    : "delete",
217
                        quotes.api().ajax.reload(function (data) {
217
                        },
218
                            if (data.recordsTotal == 0) {
218
                        /* Delete the row from the datatable */
219
                                $("#quotes").hide();
219
                        success: function(){
220
                            }
220
                            oTable.fnDeleteRow(this);
221
                            $("#quote_action_result_dialog").hide();
221
                            oTable.fnReloadAjax(null, null, true);
222
                            $("#quote_delete_success").html(_("Quote #%s deleted successfully.").format(quote_id)).show();
222
                        }
223
                        });
224
                    }).error(function () {
225
                        $("#quote_delete_error").html(_("Error deleting quote #%s. Check the logs.").format(quote_id)).show();
223
                    });
226
                    });
224
                });
227
                });
225
            } else {
228
            });
226
                return;
229
227
            }
230
        });
228
        }
229
    </script>
231
    </script>
230
[% END %]
232
[% END %]
231
233
(-)a/tools/quotes.pl (-11 / +66 lines)
Lines 1-7 Link Here
1
#!/usr/bin/perl
1
#!/usr/bin/perl
2
2
3
# Copyright 2012 Foundations Bible College Inc.
4
#
5
# This file is part of Koha.
3
# This file is part of Koha.
6
#
4
#
7
# Koha is free software; you can redistribute it and/or modify it
5
# Koha is free software; you can redistribute it and/or modify it
Lines 20-42 Link Here
20
use Modern::Perl;
18
use Modern::Perl;
21
19
22
use CGI qw ( -utf8 );
20
use CGI qw ( -utf8 );
23
use autouse 'Data::Dumper' => qw(Dumper);
21
use Try::Tiny;
24
22
25
use C4::Auth;
23
use C4::Auth;
26
use C4::Koha;
27
use C4::Context;
24
use C4::Context;
28
use C4::Output;
25
use C4::Output;
26
use Koha::Quotes;
29
27
30
my $cgi = CGI->new;
28
my $input = CGI->new;
31
29
32
my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
30
my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
33
    {
31
    {
34
        template_name   => "tools/quotes.tt",
32
        template_name => "tools/quotes.tt",
35
        query           => $cgi,
33
        query         => $input,
36
        type            => "intranet",
34
        type          => "intranet",
37
        flagsrequired   => { tools => 'edit_quotes' },
35
        flagsrequired => { tools => 'edit_quotes' },
38
        debug           => 1,
36
        debug         => 1,
39
    }
37
    }
40
);
38
);
41
39
42
output_html_with_http_headers $cgi, $cookie, $template->output;
40
my $id = $input->param('id');
41
my $op = $input->param('op') || 'list';
42
my @messages;
43
44
if ( $op eq 'add_form' ) {
45
    $template->param( quote => Koha::Quotes->find($id), );
46
}
47
elsif ( $op eq 'add_validate' ) {
48
    my @fields = qw(
49
      source
50
      text
51
    );
52
53
    if ($id) {
54
        my $quote = Koha::Quotes->find($id);
55
        for my $field (@fields) {
56
            $quote->$field( scalar $input->param($field) );
57
        }
58
59
        try {
60
            $quote->store;
61
            push @messages, { type => 'message', code => 'success_on_update' };
62
        }
63
        catch {
64
            push @messages, { type => 'alert', code => 'error_on_update' };
65
        }
66
    }
67
    else {
68
        my $quote = Koha::Quote->new(
69
            {
70
                id => $id,
71
                ( map { $_ => scalar $input->param($_) || undef } @fields )
72
            }
73
        );
74
75
        try {
76
            $quote->store;
77
            push @messages, { type => 'message', code => 'success_on_insert' };
78
        }
79
        catch {
80
            push @messages, { type => 'alert', code => 'error_on_insert' };
81
        };
82
    }
83
    $op = 'list';
84
}
85
else {
86
    $op = 'list';
87
}
88
89
$template->param( quotes_count => Koha::Quotes->search->count )
90
  if $op eq 'list';
91
92
$template->param(
93
    messages => \@messages,
94
    op       => $op,
95
);
96
97
output_html_with_http_headers $input, $cookie, $template->output;
(-)a/tools/quotes/quotes-upload_ajax.pl (-65 lines)
Lines 1-65 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2012 Foundations Bible College Inc.
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use CGI qw ( -utf8 );
23
use JSON;
24
use URI::Escape;
25
use autouse 'Data::Dumper' => qw(Dumper);
26
27
use C4::Auth;
28
use C4::Koha;
29
use C4::Context;
30
use C4::Output;
31
32
my $cgi = CGI->new;
33
my $dbh = C4::Context->dbh;
34
35
my ( $status, $cookie, $sessionID ) = C4::Auth::check_api_auth( $cgi, { tools => 'edit_quotes' } );
36
unless ($status eq "ok") {
37
    print $cgi->header(-type => 'application/json', -status => '403 Forbidden');
38
    print to_json({ auth_status => $status });
39
    exit 0;
40
}
41
42
my $success = 'true';
43
my $quotes_tmp = uri_unescape( $cgi->param('quote' ) );
44
my $quotes = decode_json( $quotes_tmp );
45
46
my $action = $cgi->param('action');
47
48
my $sth = $dbh->prepare('INSERT INTO quotes (source, text) VALUES (?, ?);');
49
50
my $insert_count = 0;
51
52
foreach my $quote (@$quotes) {
53
    $insert_count++ if $sth->execute($quote->[1], $quote->[2]);
54
    if ($sth->err) {
55
        warn sprintf('Database returned the following error: %s', $sth->errstr);
56
        $success = 'false';
57
    }
58
}
59
60
print $cgi->header('application/json');
61
62
print to_json({
63
                success => $success,
64
                records => $insert_count,
65
});
(-)a/tools/quotes/quotes_ajax.pl (-110 lines)
Lines 1-109 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2012 Foundations Bible College Inc.
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use CGI qw ( -utf8 );
23
use JSON;
24
use autouse 'Data::Dumper' => qw(Dumper);
25
26
use C4::Auth;
27
use C4::Context;
28
29
my $cgi = CGI->new;
30
my $dbh = C4::Context->dbh;
31
my $sort_columns = ["id", "source", "text", "timestamp"];
32
33
my ( $status, $cookie, $sessionID ) = C4::Auth::check_api_auth( $cgi, { tools => 'edit_quotes' } );
34
unless ($status eq "ok") {
35
    print $cgi->header(-type => 'application/json', -status => '403 Forbidden');
36
    print to_json({ auth_status => $status });
37
    exit 0;
38
}
39
40
# NOTE: This is a collection of ajax functions for use with tools/quotes.pl
41
42
my $params = $cgi->Vars; # NOTE: Multivalue parameters NOT allowed!!
43
44
print $cgi->header('application/json; charset=utf-8');
45
46
my $action = $params->{'action'} || 'get';
47
if ($action eq 'add') {
48
    my $sth = $dbh->prepare('INSERT INTO quotes (source, text) VALUES (?, ?);');
49
    $sth->execute($params->{'source'}, $params->{'text'});
50
    if ($sth->err) {
51
        warn sprintf('Database returned the following error: %s', $sth->errstr);
52
        exit 0;
53
    }
54
    my $new_quote_id = $dbh->{q{mysql_insertid}}; # ALERT: mysqlism here
55
    $sth = $dbh->prepare('SELECT * FROM quotes WHERE id = ?;');
56
    $sth->execute($new_quote_id);
57
    print to_json($sth->fetchall_arrayref, {utf8 =>1});
58
    exit 0;
59
}
60
elsif ($action eq 'edit') {
61
    my $aaData = [];
62
    my $editable_columns = [qw(source text)]; # pay attention to element order; these columns match the quotes table columns
63
    my $sth = $dbh->prepare("UPDATE quotes SET $editable_columns->[$params->{'column'}-1]  = ? WHERE id = ?;");
64
    $sth->execute($params->{'value'}, $params->{'id'});
65
    if ($sth->err) {
66
        warn sprintf('Database returned the following error: %s', $sth->errstr);
67
        exit 1;
68
    }
69
    $sth = $dbh->prepare("SELECT $editable_columns->[$params->{'column'}-1] FROM quotes WHERE id = ?;");
70
    $sth->execute($params->{'id'});
71
    $aaData = $sth->fetchrow_array();
72
    print Encode::encode('utf8', $aaData);
73
74
    exit 0;
75
}
76
elsif ($action eq 'delete') {
77
    my $sth = $dbh->prepare("DELETE FROM quotes WHERE id = ?;");
78
    $sth->execute($params->{'id'});
79
    if ($sth->err) {
80
        warn sprintf('Database returned the following error: %s', $sth->errstr);
81
        exit 1;
82
    }
83
    exit 0;
84
}
85
else {
86
    my $aaData = [];
87
    my $iTotalRecords = '';
88
    my $sth = '';
89
90
    $iTotalRecords = $dbh->selectrow_array('SELECT count(*) FROM quotes;');
91
    $sth = $dbh->prepare("SELECT * FROM quotes;");
92
93
    $sth->execute();
94
    if ($sth->err) {
95
        warn sprintf('Database returned the following error: %s', $sth->errstr);
96
        exit 1;
97
    }
98
99
    $aaData = $sth->fetchall_arrayref;
100
    my $iTotalDisplayRecords = $iTotalRecords; # no filtering happening here
101
102
103
    print to_json({
104
                    iTotalRecords       =>  $iTotalRecords,
105
                    iTotalDisplayRecords=>  $iTotalDisplayRecords,
106
                    sEcho               =>  $params->{'sEcho'},
107
                    aaData              =>  $aaData,
108
                  }, {utf8 =>1});
109
}
110
- 

Return to bug 27251