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

(-)a/koha-tmpl/intranet-tmpl/prog/css/src/staff-global.scss (+20 lines)
Lines 4618-4623 div .suggestion_note { Link Here
4618
    }
4618
    }
4619
}
4619
}
4620
4620
4621
#camera, #output {
4622
    border: 8px solid #EDF4F6;
4623
    padding: 1em;
4624
}
4625
4626
#photo {
4627
    display: block;
4628
    margin: auto;
4629
}
4630
4631
#camera-error {
4632
    display: none;
4633
    flex-direction: row;
4634
    flex-wrap: nowrap;
4635
4636
    div {
4637
        padding: 0 .5em;
4638
    }
4639
}
4640
4621
@media (min-width: 200px) {
4641
@media (min-width: 200px) {
4622
4642
4623
}
4643
}
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc (-3 / +3 lines)
Lines 38-49 Link Here
38
            [% IF ( patron.image ) %]
38
            [% IF ( patron.image ) %]
39
                <img src="/cgi-bin/koha/members/patronimage.pl?borrowernumber=[% patron.borrowernumber | uri %]" class="patronimage" alt="[% patron.firstname | html %] [% patron.surname | html %] ([% patron.cardnumber | html %])" />
39
                <img src="/cgi-bin/koha/members/patronimage.pl?borrowernumber=[% patron.borrowernumber | uri %]" class="patronimage" alt="[% patron.firstname | html %] [% patron.surname | html %] ([% patron.cardnumber | html %])" />
40
                <div class="patronimage-controls">
40
                <div class="patronimage-controls">
41
                    <div class="patronimage-control"><a data-borrowernumber="[% patron.borrowernumber | uri %]" class="btn btn-default edit-patronimage" title="Edit patron image" href="#"><i class="fa fa-pencil"></i> Edit</a></div>
41
                    <div class="patronimage-control"><a data-borrowernumber="[% patron.borrowernumber | uri %]" data-cardnumber="[% patron.cardnumber | html %]" class="btn btn-default edit-patronimage" title="Patron photo" href="#"><i class="fa fa-pencil"></i> Edit</a></div>
42
                </div>
42
                </div>
43
            [% ELSE %]
43
            [% ELSE %]
44
                <div class="patronimage empty"></div>
44
                <div class="patronimage empty"></div>
45
                <div class="patronimage-controls">
45
                <div class="patronimage-controls">
46
                    <div class="patronimage-control"><a data-borrowernumber="[% patron.borrowernumber | uri %]" class="btn btn-default edit-patronimage" title="Add patron image" href="#"><i class="fa fa-plus"></i> Add</a></div>
46
                    <div class="patronimage-control"><a data-borrowernumber="[% patron.borrowernumber | uri %]" data-cardnumber="[% patron.cardnumber | html %]" class="btn btn-default edit-patronimage" title="Patron photo" href="#"><i class="fa fa-plus"></i> Add</a></div>
47
                </div>
47
                </div>
48
            [% END %]
48
            [% END %]
49
        </div>
49
        </div>
Lines 186-192 Link Here
186
        <div class="modal-content">
186
        <div class="modal-content">
187
            <div class="modal-header">
187
            <div class="modal-header">
188
                <button type="button" class="closebtn" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
188
                <button type="button" class="closebtn" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
189
                <h4 class="modal-title" id="patronImageEditLabel">Modal title</h4>
189
                <h4 class="modal-title" id="patronImageEditLabel">Patron photo</h4>
190
            </div>
190
            </div>
191
            <div class="modal-body">
191
            <div class="modal-body">
192
                <img src="[% interface | html %]/[% theme | html %]/img/spinner-small.gif" alt="" />
192
                <img src="[% interface | html %]/[% theme | html %]/img/spinner-small.gif" alt="" />
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/str/members-menu.inc (-1 / +5 lines)
Lines 1-7 Link Here
1
[% USE raw %]
1
[% USE scalar %]
2
[% USE scalar %]
2
[% USE Koha %]
3
[% USE Koha %]
3
[% USE Categories %]
4
[% USE Categories %]
4
<!-- str/members-menu.inc -->
5
<!-- str/members-menu.inc -->
6
[% IF ( Koha.Preference('patronimages') ) %]
7
    [% Asset.js("js/patron-webcam.js") | $raw %]
8
[% END %]
5
<script>
9
<script>
6
    var advsearch = "[% advsearch | html %]";
10
    var advsearch = "[% advsearch | html %]";
7
    var destination = "[% destination | html %]";
11
    var destination = "[% destination | html %]";
Lines 26-32 Link Here
26
        $("body").on("click", "#delpicture", function(){
30
        $("body").on("click", "#delpicture", function(){
27
             return confirm(_("Are you sure you want to delete this patron image? This cannot be undone."));
31
             return confirm(_("Are you sure you want to delete this patron image? This cannot be undone."));
28
        });
32
        });
29
        $('#manage-patron-image').find("input[value*=Upload]").click(function(){
33
        $('#upload-patron-image').find("input[value*=Upload]").click(function(){
30
            if($("#uploadfile").val() == ""){
34
            if($("#uploadfile").val() == ""){
31
                alert(_("Please choose a file to upload"));
35
                alert(_("Please choose a file to upload"));
32
                return false;
36
                return false;
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/housebound.tt (-1 lines)
Lines 436-442 Link Here
436
436
437
[% MACRO jsinclude BLOCK %]
437
[% MACRO jsinclude BLOCK %]
438
    [% INCLUDE 'calendar.inc' %]
438
    [% INCLUDE 'calendar.inc' %]
439
    [% Asset.js("js/members-menu.js") | $raw %]
440
    <script>
439
    <script>
441
        $(document).ready(function() {
440
        $(document).ready(function() {
442
            $("a.delete").click(function(){
441
            $("a.delete").click(function(){
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember-patronimage.tt (+83 lines)
Line 0 Link Here
1
[% USE Koha %]
2
3
[% IF ( Koha.Preference('patronimages') ) %]
4
    [% IF ( CAN_user_tools_batch_upload_patron_images ) %]
5
        <div id="upload-patron-image" class="patroninfo-section">
6
            <h5>Upload patron photo</h5>
7
            <form method="post" id="picture-upload" action="/cgi-bin/koha/tools/picture-upload.pl"
8
                enctype="multipart/form-data">
9
                [% IF ( patron.image ) %]
10
                    <div class="hint">
11
                        To update the image for [% patron.title | html %] [% patron.firstname | html %] [% patron.surname | html %], select a new image file and click 'Upload.' <br />Click the 'Delete' button to
12
                        remove the current image.
13
                    </div>
14
                [% ELSE %]
15
                    <div class="hint">
16
                        [% patron.title | html %] [% patron.firstname | html %] [% patron.surname | html %] does not currently have an image available. To import an image for [% patron.title | html %] [% patron.firstname | html %] [% patron.surname | html %], enter the name of an image file to upload.
17
                    </div>
18
                [% END %]
19
                <p>Only PNG, GIF, JPEG, XPM formats are supported.</p>
20
                <label for="uploadfile">Select the file to upload: </label>
21
                <input type="file" id="uploadfile" name="uploadfile" required="required" />
22
                <div class="action">
23
                    <input type="hidden" id="csrf_token" name="csrf_token" value="[% csrf_token | html %]" />
24
                    <input type="hidden" id="image" name="filetype" value="image" />
25
                    <input type="hidden" id="cardnumber" name="cardnumber" value="[% patron.cardnumber | html %]" />
26
                    <input type="hidden" id="borrowernumber" name="borrowernumber" value="[% patron.borrowernumber | html %]" />
27
                    <button type="submit" class="btn btn-default btn-xs"><i class="fa fa-upload"></i> Upload photo</button>
28
                    <input name="op" type="hidden" value="Upload" />
29
                    [% IF ( patron.image ) %]
30
                        <a id="delpicture"
31
                            href="/cgi-bin/koha/tools/picture-upload.pl?op=Delete&amp;borrowernumber=[% patron.borrowernumber | html %]&amp;csrf_token=[% csrf_token | html %]"
32
                            class="btn btn-default btn-xs delete"><i class="fa fa-trash"></i> Delete</a>
33
                    [% END %]
34
                </div>
35
            </form>
36
        </div>
37
        <div id="capture-patron-image" class="patroninfo-section">
38
            <h5>Take patron photo</h5>
39
            <form method="post" id="camera-upload" action="/cgi-bin/koha/tools/picture-upload.pl">
40
                <div class="btn-toolbar">
41
                    <div class="btn-group">
42
                        <button class="btn btn-default" id="takebutton"><i class="fa fa-camera"></i> Take photo</button>
43
                    </div>
44
                    <div class="btn-group">
45
                        <button class="btn btn-default" id="retakebutton" style="display:none;"><i class="fa fa-refresh"></i> Retake photo</button>
46
                    </div>
47
                    <div class="btn-group">
48
                        <a id="downloadbutton" href="#" class="btn btn-default" style="display:none;"><i class="fa fa-download"></i> Download photo</a>
49
                    </div>
50
                    <div class="btn-group">
51
                        <button id="savebutton" type="submit" class="btn btn-default" style="display:none;"><i class="fa fa-hdd-o"></i> Upload</button>
52
                    </div>
53
                </div>
54
                <div id="camera">
55
                    <video id="viewfinder">Video stream not available.</video>
56
                </div>
57
                <canvas id="canvas" style="display:none"></canvas>
58
                <div id="output" style="display:none">
59
                    <img style="width:100%;height:auto;" id="photo" alt="The screen capture will appear in this box.">
60
                </div>
61
                <input type="hidden" name="uploadfilename" value="patron-photo.jpg" />
62
                <textarea id="uploadfiletext" name="uploadfiletext" style="display:none;"></textarea>
63
                <input type="hidden" name="csrf_token" value="[% csrf_token | html %]" />
64
                <input type="hidden" name="filetype" value="image" />
65
                <input type="hidden" name="cardnumber" value="[% patron.cardnumber | html %]" />
66
                <input type="hidden" name="borrowernumber" value="[% patron.borrowernumber | html %]" />
67
                <input name="op" type="hidden" value="Upload" />
68
            </form>
69
        </div>
70
        <div class="dialog message" style="display:none" id="camera-error">
71
            <div>
72
                <span class="fa-stack fa-lg">
73
                    <i class="fa fa-camera fa-stack-1x"></i>
74
                    <i class="fa fa-ban fa-stack-2x text-danger"></i>
75
                </span>
76
            </div>
77
            <div>
78
                <strong>Cannot take patron photo.</strong>
79
                <span id="camera-error-message"></span>
80
            </div>
81
        </div>
82
    [% END %]
83
[% END %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt (-52 lines)
Lines 256-301 Link Here
256
                                </div> [% # /div.rows %]
256
                                </div> [% # /div.rows %]
257
                            </div> [% # /div#patron-information %]
257
                            </div> [% # /div#patron-information %]
258
258
259
                            [% IF ( patronimages ) %]
260
                                [% IF ( CAN_user_tools_batch_upload_patron_images ) %]
261
                                    <div id="manage-patron-image" class="patroninfo-section">
262
                                        [% IF ( patron.image ) %]
263
                                            <div class="patroninfo-heading">
264
                                                <h3>Manage patron image</h3>
265
                                                <a class="btn btn-default btn-xs" id="show-picture-upload" href="#"><i class="fa fa-pencil"></i> Edit</a>
266
                                            </div>
267
                                        [% ELSE %]
268
                                            <div class="patroninfo-heading">
269
                                                <h3>Upload patron image</h3>
270
                                                <a class="btn btn-default btn-xs" id="show-picture-upload" href="#"><i class="fa fa-plus"></i> Add</a>
271
                                            </div>
272
                                        [% END %]
273
                                        <form method="post" id="picture-upload" style="display:none;" action="/cgi-bin/koha/tools/picture-upload.pl" enctype="multipart/form-data">
274
                                            [% IF ( patron.image ) %]
275
                                                <div class="hint">To update the image for [% patron.title | html %] [% patron.firstname | html %] [% patron.surname | html %], select a new image file and click 'Upload.' <br />Click the 'Delete' button to remove the current image.</div>
276
                                            [% ELSE %]
277
                                                <div class="hint">[% patron.title | html %] [% patron.firstname | html %] [% patron.surname | html %] does not currently have an image available. To import an image for [% patron.title | html %] [% patron.firstname | html %] [% patron.surname | html %], enter the name of an image file to upload.</div>
278
                                            [% END %]
279
                                            <p>Only PNG, GIF, JPEG, XPM formats are supported. Maximum image size is 2MB.</p>
280
                                            <label for="uploadfile">Select the file to upload: </label>
281
                                            <input type="file" id="uploadfile" name="uploadfile" required="required" />
282
                                            <div class="action">
283
                                                <input type="hidden" name="csrf_token" value="[% csrf_token | html %]" />
284
                                                <input type="hidden" id="image" name="filetype" value="image" />
285
                                                <input type="hidden" id="cardnumber" name="cardnumber" value="[% patron.cardnumber | html %]" />
286
                                                <input type="hidden" name="borrowernumber" value="[% patron.borrowernumber | html %]" />
287
                                                <button type="submit" class="btn btn-default btn-xs"><i class="fa fa-upload"></i> Upload</button>
288
                                                <input name="op" type="hidden" value="Upload" />
289
                                                [% IF ( patron.image ) %]
290
                                                    <a id="delpicture" href="/cgi-bin/koha/tools/picture-upload.pl?op=Delete&amp;borrowernumber=[% patron.borrowernumber | html %]&amp;csrf_token=[% csrf_token | html %]" class="btn btn-default btn-xs delete"><i class="fa fa-trash"></i> Delete</a>
291
                                                [% END %]
292
                                                <a href="#" id="cancel-picture-upload" class="cancel">Cancel</a>
293
                                            </div>
294
                                        </form>
295
                                    </div> [% # /div#manage-patron-image %]
296
                                [% END %]
297
                            [% END %]
298
299
                            [% IF Koha.Preference('HouseboundModule') %]
259
                            [% IF Koha.Preference('HouseboundModule') %]
300
                                <div id="houseboundroles" class="patroninfo-section">
260
                                <div id="houseboundroles" class="patroninfo-section">
301
                                    [% IF ( housebound_role.housebound_chooser == 1 OR housebound_role.housebound_deliverer == 1 ) %]
261
                                    [% IF ( housebound_role.housebound_chooser == 1 OR housebound_role.housebound_deliverer == 1 ) %]
Lines 943-960 Link Here
943
            $("#view_restrictions").on("click",function(){
903
            $("#view_restrictions").on("click",function(){
944
                $('#debarments-tab-link').click();
904
                $('#debarments-tab-link').click();
945
            });
905
            });
946
947
            $("#show-picture-upload").on("click", function(e){
948
                e.preventDefault();
949
                $(this).toggle();
950
                $("#picture-upload").toggle();
951
            });
952
953
            $("#cancel-picture-upload").on("click", function(e){
954
                e.preventDefault();
955
                $("#picture-upload, #show-picture-upload").toggle();
956
            });
957
958
         });
906
         });
959
        function uncheck_sibling(me){
907
        function uncheck_sibling(me){
960
            nodename=me.getAttribute("name");
908
            nodename=me.getAttribute("name");
(-)a/koha-tmpl/intranet-tmpl/prog/js/members-menu.js (-10 / +29 lines)
Lines 90-112 $(document).ready(function(){ Link Here
90
        $("#borrower_message").val( $(this).val() );
90
        $("#borrower_message").val( $(this).val() );
91
    });
91
    });
92
92
93
    $("#patronImageEdit").on("shown.bs.modal", function(){
94
        startup();
95
    });
96
93
    $(".edit-patronimage").on("click", function(e){
97
    $(".edit-patronimage").on("click", function(e){
94
        e.preventDefault();
98
        e.preventDefault();
95
        var borrowernumber = $(this).data("borrowernumber");
99
        var borrowernumber = $(this).data("borrowernumber");
96
        $.get("/cgi-bin/koha/members/moremember.pl", { borrowernumber : borrowernumber }, function( data ){
100
        var cardnumber = $(this).data("cardnumber");
97
            var image_form = $(data).find("#picture-upload");
98
            image_form.show().find(".cancel").remove();
99
            $("#patronImageEdit .modal-body").html( image_form );
100
        });
101
        var modalTitle = $(this).attr("title");
101
        var modalTitle = $(this).attr("title");
102
        $("#patronImageEdit .modal-title").text(modalTitle);
102
        $.ajax({
103
        $("#patronImageEdit").modal("show");
103
            url: "/cgi-bin/koha/members/moremember-patronimage.pl",
104
            type: "GET",
105
            data: { borrowernumber: borrowernumber, cardnumber: cardnumber },
106
            success: function ( data ) {
107
                $("#patronImageEdit .modal-body").html( data );
108
                $("#patronImageEdit .modal-title").text(modalTitle);
109
                $("#patronImageEdit").modal("show");
110
            },
111
            error: function () {
112
                location.href="/cgi-bin/koha/members/moremember-patronimage.pl?borrowernumber=" + borrowernumber;
113
            }
114
        });
115
        $("#patronImageEdit").on("hidden.bs.modal", function(){
116
            /* Stop using the user's camera when modal is closed */
117
            let viewfinder = document.getElementById("viewfinder");
118
            if( viewfinder.srcObject ){
119
                viewfinder.srcObject.getTracks().forEach( track => {
120
                    if( track.readyState == 'live' && track.kind === 'video'){
121
                        track.stop();
122
                    }
123
                });
124
            }
125
        });
104
    });
126
    });
105
106
});
127
});
107
128
108
109
110
function searchfield_date_tooltip(filter) {
129
function searchfield_date_tooltip(filter) {
111
    var field = "#searchmember" + filter;
130
    var field = "#searchmember" + filter;
112
    var type = "#searchfieldstype" + filter;
131
    var type = "#searchfieldstype" + filter;
(-)a/koha-tmpl/intranet-tmpl/prog/js/patron-webcam.js (+184 lines)
Line 0 Link Here
1
/* global __ */
2
/* exported startup */
3
4
/* Adapted from Mozilla's article "Taking still photos with WebRTC"
5
* https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Taking_still_photos
6
*/
7
8
var width = 480; // We will scale the photo width to this
9
var height = 0; // This will be computed based on the input stream
10
11
// |streaming| indicates whether or not we're currently streaming
12
// video from the camera. Obviously, we start at false.
13
14
var streaming = false;
15
16
// The various HTML elements we need to configure or control. These
17
// will be set by the startup() function.
18
19
var video = null;
20
var canvas = null;
21
var photo = null;
22
var takebutton = null;
23
var retakebutton = null;
24
var downloadbutton = null;
25
var savebutton = null;
26
var output = null;
27
var camera = null;
28
var uploadfiletext = null;
29
30
/**
31
 * Initiate the camera and add some click handlers
32
 */
33
34
function startup() {
35
    video = document.getElementById('viewfinder');
36
    canvas = document.getElementById('canvas');
37
    photo = document.getElementById('photo');
38
    takebutton = document.getElementById('takebutton');
39
    retakebutton = document.getElementById('retakebutton');
40
    downloadbutton = document.getElementById('downloadbutton');
41
    savebutton = document.getElementById('savebutton');
42
    output = document.getElementById("output");
43
    camera = document.getElementById("camera");
44
    uploadfiletext = document.getElementById("uploadfiletext");
45
46
    navigator.mediaDevices.getUserMedia({
47
        video: true,
48
        audio: false
49
    })
50
        .then(function (stream) {
51
            video.srcObject = stream;
52
            video.play();
53
        })
54
        .catch(function (err) {
55
            $("#capture-patron-image").hide();
56
            $("#camera-error").css("display", "flex");
57
            $("#camera-error-message").text( showMediaErrors( err ) );
58
        });
59
60
    video.addEventListener('canplay', function () {
61
        if (!streaming) {
62
            height = video.videoHeight / (video.videoWidth / width);
63
64
            // Firefox currently has a bug where the height can't be read from
65
            // the video, so we will make assumptions if this happens.
66
67
            if (isNaN(height)) {
68
                height = width / (4 / 3);
69
            }
70
71
            video.setAttribute('width', width);
72
            video.setAttribute('height', height);
73
            canvas.setAttribute('width', width);
74
            canvas.setAttribute('height', height);
75
            photo.setAttribute('width', width);
76
            photo.setAttribute('height', height);
77
            streaming = true;
78
        }
79
    }, false);
80
81
    takebutton.addEventListener('click', function (ev) {
82
        takepicture();
83
        ev.preventDefault();
84
    }, false);
85
86
    retakebutton.addEventListener('click', function (ev) {
87
        ev.preventDefault();
88
        retakephoto();
89
    }, false);
90
91
    clearphoto();
92
}
93
94
function showMediaErrors( err ){
95
    // Example error: "NotAllowedError: Permission denied"
96
    var errorcode = err.toString().split(":");
97
    var output;
98
    switch ( errorcode[0] ) {
99
    case "NotFoundError":
100
    case "DevicesNotFoundError":
101
        output = __("No camera detected.");
102
        break;
103
    case "NotReadableError":
104
    case "TrackStartError":
105
        output = __("Could not access camera.");
106
        break;
107
    case "NotAllowedError":
108
    case "PermissionDeniedError":
109
        output = __("Access to camera denied.");
110
        break;
111
    default:
112
        output = __("An unknown error occurred: ") + err;
113
        break;
114
    }
115
    return output;
116
}
117
118
/**
119
 * Clear anything passed to the canvas element and the corresponding image.
120
 */
121
122
function clearphoto() {
123
    var context = canvas.getContext('2d');
124
    context.fillStyle = "#AAA";
125
    context.fillRect(0, 0, canvas.width, canvas.height);
126
127
    var data = canvas.toDataURL('image/jpeg', 1.0);
128
    photo.setAttribute('src', data);
129
}
130
131
/**
132
 * Reset the interface to hide download and save buttons.
133
 * Redisplay camera "shutter" button.
134
 */
135
136
function retakephoto(){
137
    downloadbutton.href= "";
138
    downloadbutton.style.display = "none";
139
    takebutton.style.display = "inline-block";
140
    retakebutton.style.display = "none";
141
    savebutton.style.display = "none";
142
    output.style.display = "none";
143
    photo.src = "";
144
    camera.style.display = "block";
145
    uploadfiletext.value = "";
146
}
147
148
/**
149
 * Capture the data from the user's camera and write it to the canvas element.
150
 * The canvas data is converted to a data-url, and that URL set as the src
151
 * attribute of an image.
152
 * Display two controls for the captured photo: Download (to save to the
153
 * user's computer) and Upload (save to the patron's record in Koha).
154
 */
155
156
function takepicture() {
157
    var context = canvas.getContext('2d');
158
    var cardnumber = document.getElementById("cardnumber").value;
159
    camera.style.display = "none";
160
    downloadbutton.style.display = '';
161
    output.style.display = "block";
162
    takebutton.style.display = "none";
163
    retakebutton.style.display = "inline-block";
164
    savebutton.style.display = "inline-block";
165
    if (width && height) {
166
        canvas.width = width;
167
        canvas.height = height;
168
        context.drawImage(video, 0, 0, width, height);
169
170
        var data = canvas.toDataURL('image/jpeg', 1.0);
171
        photo.setAttribute('src', data);
172
        if( cardnumber !== '' ){
173
            // Download a file which the patrons card number as its name
174
            downloadbutton.download = cardnumber + ".jpg";
175
        } else {
176
            downloadbutton.download = "patron-photo.jpg";
177
        }
178
        downloadbutton.href = data;
179
        uploadfiletext.value = data;
180
181
    } else {
182
        clearphoto();
183
    }
184
}
(-)a/members/moremember-patronimage.pl (+59 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2020 Mark Tompsett
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
use CGI qw ( -utf8 );
22
use C4::Auth qw( get_template_and_user );
23
use C4::Output qw( output_and_exit_if_error output_and_exit output_html_with_http_headers );
24
use Koha::Patrons;
25
use Koha::Token;
26
27
my $input = CGI->new;
28
29
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
30
    {
31
        template_name   => 'members/moremember-patronimage.tt',
32
        query           => $input,
33
        type            => 'intranet',
34
        flagsrequired   => { borrowers => 'edit_borrowers' },
35
    }
36
);
37
38
my $borrowernumber = $input->param('borrowernumber');
39
my $cardnumber     = $input->param('cardnumber');
40
my $patron         = Koha::Patrons->find($borrowernumber);
41
my $logged_in_user = Koha::Patrons->find($loggedinuser);
42
43
$template->param(
44
    csrf_token => Koha::Token->new->generate_csrf(
45
        { session_id => $input->cookie('CGISESSID'), }
46
    ),
47
    patron         => $patron,
48
    logged_in_user => $logged_in_user,
49
);
50
51
output_html_with_http_headers $input, $cookie, $template->output;
52
53
__END__
54
55
=head1 moremember-patronimage.pl
56
57
 script to provide modal window for patron images
58
59
=cut
(-)a/tools/picture-upload.pl (-10 / +29 lines)
Lines 24-29 use Modern::Perl; Link Here
24
use File::Temp;
24
use File::Temp;
25
use CGI qw ( -utf8 );
25
use CGI qw ( -utf8 );
26
use GD;
26
use GD;
27
use MIME::Base64;
27
use C4::Context;
28
use C4::Context;
28
use C4::Auth qw( get_template_and_user );
29
use C4::Auth qw( get_template_and_user );
29
use C4::Output qw( output_and_exit output_html_with_http_headers );
30
use C4::Output qw( output_and_exit output_html_with_http_headers );
Lines 51-57 my ($template, $loggedinuser, $cookie) Link Here
51
52
52
our $filetype      = $input->param('filetype') || '';
53
our $filetype      = $input->param('filetype') || '';
53
my $cardnumber     = $input->param('cardnumber');
54
my $cardnumber     = $input->param('cardnumber');
54
our $uploadfilename = $input->param('uploadfile') || '';
55
our $uploadfilename = $input->param('uploadfile') || $input->param('uploadfilename') || '';
56
my $uploadfiletext = $input->param('uploadfiletext') || '';
55
my $uploadfile     = $input->upload('uploadfile');
57
my $uploadfile     = $input->upload('uploadfile');
56
my $borrowernumber = $input->param('borrowernumber');
58
my $borrowernumber = $input->param('borrowernumber');
57
my $op             = $input->param('op') || '';
59
my $op             = $input->param('op') || '';
Lines 83-89 our @counts = (); Link Here
83
our %errors = ();
85
our %errors = ();
84
86
85
# Case is important in these operational values as the template must use case to be visually pleasing!
87
# Case is important in these operational values as the template must use case to be visually pleasing!
86
if ( ( $op eq 'Upload' ) && $uploadfile ) {
88
if ( ( $op eq 'Upload' ) && ($uploadfile || $uploadfiletext) ) {
87
89
88
    output_and_exit( $input, $cookie, $template, 'wrong_csrf_token' )
90
    output_and_exit( $input, $cookie, $template, 'wrong_csrf_token' )
89
        unless Koha::Token->new->check_csrf({
91
        unless Koha::Token->new->check_csrf({
Lines 100-117 if ( ( $op eq 'Upload' ) && $uploadfile ) { Link Here
100
      File::Temp::tempfile( SUFFIX => $filesuffix, UNLINK => 1 );
102
      File::Temp::tempfile( SUFFIX => $filesuffix, UNLINK => 1 );
101
    my ( @directories, $results );
103
    my ( @directories, $results );
102
104
103
    $errors{'NOTZIP'} = 1
104
      if ( $uploadfilename !~ /\.zip$/i && $filetype =~ m/zip/i );
105
    $errors{'NOWRITETEMP'} = 1 unless ( -w $dirname );
105
    $errors{'NOWRITETEMP'} = 1 unless ( -w $dirname );
106
    $errors{'EMPTYUPLOAD'} = 1 unless ( length($uploadfile) > 0 );
106
    if ( length($uploadfiletext) == 0 ) {
107
        $errors{'NOTZIP'} = 1
108
          if ( $uploadfilename !~ /\.zip$/i && $filetype =~ m/zip/i );
109
        $errors{'EMPTYUPLOAD'} = 1 unless ( length($uploadfile) > 0 );
110
    }
107
111
108
    if (%errors) {
112
    if (%errors) {
109
        $template->param( ERRORS => [ \%errors ] );
113
        $template->param( ERRORS => [ \%errors ] );
110
        output_html_with_http_headers $input, $cookie, $template->output;
114
        output_html_with_http_headers $input, $cookie, $template->output;
111
        exit;
115
        exit;
112
    }
116
    }
113
    while (<$uploadfile>) {
117
114
        print $tfh $_;
118
    if ( length($uploadfiletext) == 0 ) {
119
        while (<$uploadfile>) {
120
            print $tfh $_;
121
        }
122
    } else {
123
        # data type controlled in toDataURL() in template
124
        if ( $uploadfiletext =~ /data:image\/jpeg;base64,(.*)/ ) {
125
            my $encoded_picture = $1;
126
            my $decoded_picture = decode_base64($encoded_picture);
127
            print $tfh $decoded_picture;
128
        } else {
129
            $errors{'BADPICTUREDATA'} = 1;
130
            $template->param( ERRORS => [ \%errors ] );
131
            output_html_with_http_headers $input, $cookie, $template->output;
132
            exit;
133
        }
115
    }
134
    }
116
    close $tfh;
135
    close $tfh;
117
    if ( $filetype eq 'zip' ) {
136
    if ( $filetype eq 'zip' ) {
Lines 221-227 sub handle_dir { Link Here
221
            return \%direrrors;
240
            return \%direrrors;
222
        }
241
        }
223
242
224
        while ( my $line = <$fh> ) {
243
        my @lines = <$fh>;
244
        close $fh;
245
        foreach my $line (@lines) {
225
            $logger->debug("Reading contents of $file");
246
            $logger->debug("Reading contents of $file");
226
            chomp $line;
247
            chomp $line;
227
            $logger->debug("Examining line: $line");
248
            $logger->debug("Examining line: $line");
Lines 241-247 sub handle_dir { Link Here
241
            $source = "$dir/$filename";
262
            $source = "$dir/$filename";
242
            %counts = handle_file( $cardnumber, $source, $template, %counts );
263
            %counts = handle_file( $cardnumber, $source, $template, %counts );
243
        }
264
        }
244
        close $fh;
245
        closedir DIR;
265
        closedir DIR;
246
    }
266
    }
247
    else {
267
    else {
248
- 

Return to bug 6815