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

(-)a/installer/data/mysql/atomicupdate/bug_14242-autofill_syspref.perl (+6 lines)
Line 0 Link Here
1
$DBversion = 'XXX';
2
if( CheckVersion( $DBversion ) ) {
3
    $dbh->do(q{ INSERT IGNORE INTO systempreferences (`variable`, `value`, `options`, `explanation`,`type`) VALUES ('OPACSuggestionAutoFill', '0', NULL, 'Automatically fill OPAC suggestion form with data from Google Books API','YesNo') });
4
5
    NewVersion( $DBversion, 14242, "Add OPACSuggestionAutoFill system preference");
6
}
(-)a/installer/data/mysql/mandatory/sysprefs.sql (+1 lines)
Lines 386-391 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
386
('OAI-PMH:DeletedRecord','persistent','Koha\'s deletedbiblio table will never be deleted (persistent), might be deleted (transient), or will never have any data in it (no)','transient|persistent|no','Choice'),
386
('OAI-PMH:DeletedRecord','persistent','Koha\'s deletedbiblio table will never be deleted (persistent), might be deleted (transient), or will never have any data in it (no)','transient|persistent|no','Choice'),
387
('OAI-PMH:MaxCount','50',NULL,'OAI-PMH maximum number of records by answer to ListRecords and ListIdentifiers queries','Integer'),
387
('OAI-PMH:MaxCount','50',NULL,'OAI-PMH maximum number of records by answer to ListRecords and ListIdentifiers queries','Integer'),
388
('OPACAcquisitionDetails','0','','Show the acquisition details at the OPAC','YesNo'),
388
('OPACAcquisitionDetails','0','','Show the acquisition details at the OPAC','YesNo'),
389
('OPACSuggestionAutoFill','0',NULL,'Automatically fill OPAC suggestion form with data from Google Books API','YesNo'),
389
('OpacAddMastheadLibraryPulldown','0','','Adds a pulldown menu to select the library to search on the opac masthead.','YesNo'),
390
('OpacAddMastheadLibraryPulldown','0','','Adds a pulldown menu to select the library to search on the opac masthead.','YesNo'),
390
('OpacAdvSearchMoreOptions','pubdate,itemtype,language,subtype,sorting,location','Show search options for the expanded view (More options)','pubdate|itemtype|language|subtype|sorting|location','multiple'),
391
('OpacAdvSearchMoreOptions','pubdate,itemtype,language,subtype,sorting,location','Show search options for the expanded view (More options)','pubdate|itemtype|language|subtype|sorting|location','multiple'),
391
('OpacAdvSearchOptions','pubdate,itemtype,language,sorting,location','Show search options','pubdate|itemtype|language|subtype|sorting|location','multiple'),
392
('OpacAdvSearchOptions','pubdate,itemtype,language,sorting,location','Show search options','pubdate|itemtype|language|subtype|sorting|location','multiple'),
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/opac.pref (+7 lines)
Lines 520-525 OPAC: Link Here
520
                  0: Don't display
520
                  0: Don't display
521
            - a library selection list for news items in the OPAC.
521
            - a library selection list for news items in the OPAC.
522
        -
522
        -
523
            - pref: OPACSuggestionAutoFill
524
              default: 0
525
              choices:
526
                  yes: Enable
527
                  no: Disable
528
            - OPAC suggestions form automatically filling with data from Google Books API.
529
        -
523
            - "Use the following as the OPAC ISBD template:"
530
            - "Use the following as the OPAC ISBD template:"
524
            - pref: OPACISBD
531
            - pref: OPACISBD
525
              type: textarea
532
              type: textarea
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-suggestions.tt (-6 / +30 lines)
Lines 75-80 Link Here
75
                            <form action="/cgi-bin/koha/opac-suggestions.pl" method="post" id="add_suggestion_form">
75
                            <form action="/cgi-bin/koha/opac-suggestions.pl" method="post" id="add_suggestion_form">
76
                                <fieldset class="rows">
76
                                <fieldset class="rows">
77
                                    <ol>
77
                                    <ol>
78
                                        [% IF Koha.Preference( 'OPACSuggestionAutoFill' ) %]
79
                                            <li>
80
                                                <label for="isbn">Standard number (ISBN, ISSN or other):</label>
81
                                                <input type="text" id="isbn" name="isbn" maxlength="80" style="margin-right:10px;margin-top:10px;" size="15"/>
82
                                            </li>
83
                                        [% END %]
78
                                        <li>
84
                                        <li>
79
                                            [% IF ( title_required ) %]
85
                                            [% IF ( title_required ) %]
80
                                                <label for="title" class="required">Title:</label>
86
                                                <label for="title" class="required">Title:</label>
Lines 101-117 Link Here
101
                                            <li>
107
                                            <li>
102
                                                <div title="Copyright or publication year, for example: 2016">
108
                                                <div title="Copyright or publication year, for example: 2016">
103
                                                    [% IF ( copyrightdate_required ) %]
109
                                                    [% IF ( copyrightdate_required ) %]
104
                                                        <label for="copyrightdate" class="required">Copyright date:</label>
110
                                                        <label for="copyrightdate" class="required">Publication year:</label>
105
                                                        <input type="text" id="copyrightdate" name="copyrightdate" pattern="[12]\d{3}" size="4" maxlength="4" value="[% copyrightdate | html %]" required="required" />
111
                                                        <input type="text" id="copyrightdate" name="copyrightdate" pattern="[12]\d{3}" size="10" maxlength="4" value="[% copyrightdate | html %]" required="required" />
106
                                                    <div class="required_label required">Required</div>
112
                                                        <div class="required_label required">Required</div>
107
                                                    [% ELSE %]
113
                                                    [% ELSE %]
108
                                                        <label for="copyrightdate">Copyright date:</label>
114
                                                        <label for="copyrightdate">Publication year:</label>
109
                                                        <input type="text" id="copyrightdate" name="copyrightdate" pattern="[12]\d{3}" size="4" maxlength="4" value="[% copyrightdate | html %]" />
115
                                                        <input type="text" id="copyrightdate" name="copyrightdate" pattern="[12]\d{3}" size="10" maxlength="4" value="[% copyrightdate | html %]" />
110
                                                    [% END %]
116
                                                    [% END %]
117
                                                    <span class="hint">Copyright or publication year, for example: 2016</span>
111
                                                </div>
118
                                                </div>
112
                                            </li>
119
                                            </li>
113
                                        [% END %]
120
                                        [% END %]
114
                                        [% UNLESS ( isbn_hidden )%]
121
                                        [% UNLESS ( isbn_hidden or Koha.Preference('OPACSuggestionAutoFill') ) %]
115
                                            <li>
122
                                            <li>
116
                                                [% IF ( isbn_required ) %]
123
                                                [% IF ( isbn_required ) %]
117
                                                    <label for="isbn" class="required">Standard number (ISBN, ISSN or other):</label>
124
                                                    <label for="isbn" class="required">Standard number (ISBN, ISSN or other):</label>
Lines 489-494 Link Here
489
496
490
[% INCLUDE 'opac-bottom.inc' %]
497
[% INCLUDE 'opac-bottom.inc' %]
491
[% BLOCK jsinclude %]
498
[% BLOCK jsinclude %]
499
[% IF ( Koha.Preference("OPACSuggestionAutoFill") ) %]
500
    [% Asset.js("js/autofill.js") | $raw %]
501
[% END %]
492
[% INCLUDE 'datatables.inc' %]
502
[% INCLUDE 'datatables.inc' %]
493
<script>
503
<script>
494
    [% IF ( loggedinusername ) %]
504
    [% IF ( loggedinusername ) %]
Lines 505-510 Link Here
505
        }
515
        }
506
    [% END %]
516
    [% END %]
507
517
518
    [% IF Koha.Preference( 'OPACSuggestionAutoFill' ) %]
519
    $(function() {
520
        $('#isbn').autofill({
521
            'volumeInfo.title': {target: 'title', effect: 'flash'},
522
            'volumeInfo.authors': {target: 'author', effect: 'flash'},
523
            'volumeInfo.publisher': {target: 'publishercode', effect: 'flash'},
524
            'volumeInfo.publishedDate': {target: 'copyrightdate', effect: 'flash'},
525
            /* google books api seem to only have books, so if we got a result
526
             * item, type will be a book, so set to BK (book). */
527
            'kind': {target: 'itemtype', handle: function(t,v) { t.val('BK'); },},
528
        });
529
    });
530
    [% END %]
531
508
    $(function() {
532
    $(function() {
509
        $("#suggestt").dataTable($.extend(true, {}, dataTablesDefaults, {
533
        $("#suggestt").dataTable($.extend(true, {}, dataTablesDefaults, {
510
            "order": [[ 1, "asc" ]],
534
            "order": [[ 1, "asc" ]],
(-)a/koha-tmpl/opac-tmpl/bootstrap/js/autofill.js (-1 / +209 lines)
Line 0 Link Here
0
- 
1
/*
2
 * Plugin dealing with the Google API based on jQuery autofill plugin
3
 * https://plugins.jquery.com/autofill/
4
 *
5
 * Automatically fills form inputs with relevant data based on search result
6
 * Modified for OPAC Suggestion form
7
 */
8
9
(function ($) {
10
    function typeString(o) {
11
        if (typeof o != 'object')
12
            return typeof o;
13
14
        if (o === null)
15
            return "null";
16
17
        //object, array, function, date, regexp, string, number, boolean, error
18
        var internalClass = Object.prototype.toString.call(o)
19
            .match(/\[object\s(\w+)\]/)[1];
20
21
        return internalClass.toLowerCase();
22
    }
23
24
    var AutoFiller = function (elm, fields, type) {
25
        var self = this;
26
        self.type = type;
27
        self.$elm = $(elm);
28
        self.fields = fields;
29
30
        var MSG_UNDO_AUTOFILL_SUGGESTION =  __("Clear form");
31
        var MSG_SEARCH_GOOGLE_BOOKS = __("Search Google Books");
32
33
        /* decorate element as autofiller */
34
        self.$undo = $('<input type="button" class="btn btn-info btn-sm" style="display:none;margin-left:5px;" value="' + MSG_UNDO_AUTOFILL_SUGGESTION + '" />');
35
        self.$fillbtn = $('<input type="button" class="btn btn-primary btn-sm" value="' + MSG_SEARCH_GOOGLE_BOOKS + '" />');
36
        self.$error = $('<span class="add-on" style="display:none;padding-left:5px;"></span>');
37
        self.$elm.after(self.$error);
38
        self.$elm.after(self.$undo);
39
        self.$elm.after(self.$fillbtn);
40
41
        for(var key in fields) {
42
            if( fields.hasOwnProperty(key) && typeof fields[key] === 'object' ) {
43
                var $target = $('#' + self.fields[key].target);
44
                self.fields[key].$target = $target;
45
            }
46
        }
47
48
        self.$fillbtn.click(function(){
49
            /* clear fields first */
50
            for(var key in self.fields) {
51
                var field = self.fields[key];
52
                field.$target.trigger('autofill-undo');
53
            }
54
            /* only allow forced update once every second */
55
            if(Date.now() - self.lastupdate > 1000) {
56
                self.$elm.trigger('change');
57
            }
58
        });
59
60
        self.$undo.click(function(){
61
            for(var key in self.fields) {
62
                var field = self.fields[key];
63
                //field.$target.val("");
64
                field.$target.trigger('autofill-undo');
65
            }
66
            self.$undo.hide();
67
        });
68
69
        self.$elm.change(function() {
70
            self.lastupdate = Date.now();
71
            self.$error.html('');
72
            self.$error.hide();
73
            /* give user some feedback that the request is in progress */
74
            self.$fillbtn.fadeOut(1000).fadeIn(1000);
75
            if ( self.$elm.val()) {
76
                var gAPI = 'https://www.googleapis.com/books/v1/volumes?q=';
77
                gAPI += self.$elm.val().replace(/\-/g, '');
78
                gAPI += '&maxResults=1';
79
                $.getJSON(gAPI, function (response) {
80
                    if(response.totalItems == 0) {
81
                        self.$error.html(__('Sorry, nothing found.'));
82
                        self.$error.show();
83
                        return;
84
                    }
85
86
                    var undos = 0;
87
                    var item = response.items[0];
88
                    for(var key in self.fields) {
89
                        var filled = false;
90
                        var value = eval('item.'+key);
91
                        var field = self.fields[key];
92
93
                        /* field handled by caller */
94
                        if('handle' in field) {
95
                            if(typeof field.handle === 'function')
96
                                field.handle(field.$target, value);
97
98
                            continue; /* next please */
99
                        }
100
101
                        /* wouldn't know what to do with result unless we have a
102
                        * target */
103
                        if( ! field.$target )
104
                            continue;
105
106
                        /* handle differently depending on datatype */
107
                        switch(typeString(value)) {
108
                            case 'array':
109
                                switch(field.$target.prop('nodeName').toUpperCase()) {
110
                                    case 'TEXTAREA':
111
                                        undos++;
112
                                        field.$target.bind('autofill-undo', field.$target.text(), function(e){$(this).text(e.data);});
113
                                        field.$target.text(value.join(', '));
114
                                        break;
115
                                    case 'INPUT':
116
                                    default:
117
                                        undos++;
118
                                        field.$target.bind('autofill-undo', field.$target.val(), function(e){$(this).val(e.data);});
119
                                        field.$target.val(value.join(', '));
120
                                        break;
121
                                }
122
                                break;
123
                            default:
124
                                switch(field.$target.prop('nodeName').toUpperCase()) {
125
                                    case 'TEXTAREA':
126
                                        undos++;
127
                                        field.$target.bind('autofill-undo', field.$target.text(), function(e){$(this).text(e.data);});
128
                                        field.$target.text(value);
129
                                        break;
130
                                    case 'SELECT':
131
                                    case 'INPUT':
132
                                    default:
133
                                        undos++;
134
                                        field.$target.bind('autofill-undo', field.$target.val(), function(e){$(this).val(e.data);});
135
                                        field.$target.val(value);
136
                                        break;
137
                                }
138
                        }
139
140
                        switch(field.effect) {
141
                            case 'flash':
142
                                field.$target.fadeOut(500).fadeIn(500);
143
                                break;
144
                        }
145
                    }
146
147
                    if(undos > 0)
148
                        self.$undo.show();
149
150
                });
151
                }
152
           });
153
       };
154
155
    /*
156
     * @fields object: Google Books API item propreties map for
157
     *                 mapping against a target element. Expected
158
     *                 type:
159
     *                 {
160
     *                  GoogleBooksItem.property: {target: ELEM,
161
     *                                             handle: function(target, value),
162
     *                                             effect: jQuery effects,
163
     *                                            }
164
     *                 }
165
     *
166
     *                 "target" is optional and if specified alone (i.e no
167
     *                 handle proprety) autofill will automaticly fill this
168
     *                 target element with returned data.
169
     *
170
     *                 "handle" is optional and will be called when ajax request
171
     *                 has finished and target is matched. Function specifies
172
     *                 two arguments: target and value. Target is the target
173
     *                 element specified by "target" and value is the value
174
     *                 returned by Google Books API for the matched property.
175
     *
176
     *                 If a handle function is given, full control of result data
177
     *                 is given to the handle function.
178
     *
179
     *                 "effect" is optional and specifies effect name of effect
180
     *                 to use for the target once value has been set. Can be one of:
181
     *
182
     *                      - 'flash'
183
     *
184
     * @type string: defines the query type, default to input name
185
     *               For example <input type="text" name="isbn"></input>
186
     *               will search for isbn by default
187
     *
188
     * @EXAMPLE
189
     *
190
     *  $('#isbn').autofill({
191
     *      'volumeInfo.title': {target: 'title', effect: 'flash'},
192
     *      'volumeInfo.authors': {target: 'author'},
193
     *      'volumeInfo.publisher': {target: 'publishercode'},
194
     *      'selfLink': {handle: function(t,v){window.location=v;}}
195
     *  });
196
     * */
197
    $.fn.autofill = function(fields, type) {
198
        if(type === undefined) // default to input name
199
            type = this.attr('name');
200
201
        return this.each(function(i){
202
            var plugin = $.data(this, "plugin_autofill");
203
            if (plugin)
204
                plugin.destroy();
205
206
            $.data(this, "plugin_autofill", new AutoFiller(this, fields, type));
207
        });
208
    };
209
}(jQuery));

Return to bug 14242