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

(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/circ-menu.inc (+7 lines)
Lines 149-154 Link Here
149
            <div class="modal-body">
149
            <div class="modal-body">
150
                <img src="[% interface | html %]/[% theme | html %]/img/spinner-small.gif" alt="" />
150
                <img src="[% interface | html %]/[% theme | html %]/img/spinner-small.gif" alt="" />
151
            </div>
151
            </div>
152
            <form method="post" action="/cgi-bin/koha/tools/picture-upload.pl" enctype="multipart/form-data">
153
                <input type="hidden" id="webcam_csrf_token" name="csrf_token" value="[% csrf_token | html %]" />
154
                <input type="hidden" id="webcam_image" name="filetype" value="image" />
155
                <input type="hidden" id="webcam_cardnumber" name="cardnumber" value="[% patron.cardnumber | html %]" />
156
                <input type="hidden" id="webcam_borrowernumber" name="borrowernumber" value="[% patron.borrowernumber | html %]" />
157
                <input id="webcam_op" name="op" type="hidden" value="Upload" />
158
            </form>
152
            <div class="modal-footer">
159
            <div class="modal-footer">
153
                <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
160
                <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
154
            </div>
161
            </div>
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/intranet-bottom.inc (-18 lines)
Lines 68-91 Link Here
68
    [% END %]
68
    [% END %]
69
[% END %]
69
[% END %]
70
    <span id="audio-alert"></span>
70
    <span id="audio-alert"></span>
71
72
    <script>
73
        function detectWebcam(callback) {
74
            let md = navigator.mediaDevices;
75
            if (!md || !md.enumerateDevices) return callback(false);
76
            md.enumerateDevices().then(devices => {
77
                callback(devices.some(device => 'videoinput' === device.kind));
78
            })
79
        }
80
81
        detectWebcam(function(hasWebcam) {
82
            let div_take_image = document.getElementById('take_patron_image');
83
            if (div_take_image) {
84
                div_take_image.style.display = hasWebcam ? "block" : "none";
85
            }
86
        });
87
    </script>
88
89
    [% IF ( footerjs ) %]
71
    [% IF ( footerjs ) %]
90
        [% INCLUDE js_includes.inc %]
72
        [% INCLUDE js_includes.inc %]
91
        [% jsinclude | $raw # Parse the page template's JavaScript block if necessary %]
73
        [% jsinclude | $raw # Parse the page template's JavaScript block if necessary %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/member-webcam.tt (-107 / +33 lines)
Lines 1-107 Link Here
1
<!DOCTYPE html>
1
[% INCLUDE 'doc-head-open.inc' %]
2
<html lang="en" dir="ltr">
2
<title>Koha &rsaquo; Patrons &rsaquo; Capture patron image</title>
3
3
[% INCLUDE 'doc-head-close.inc' %]
4
  <head>
4
<style>#snap{width:100%;background-color:#336699;color:white;}</style>
5
    <meta charset="utf-8" />
5
</head>
6
    <title>Koha Patron Image Via Webcam</title>
6
7
    <meta name="description"
7
<body id="pat_take_photo" class="pat">
8
      content="Attempt to take a patron picture using the webcam" />
8
[% INCLUDE 'header.inc' %]
9
  </head>
9
[% INCLUDE 'patron-search.inc' %]
10
10
11
  <body>
11
<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/members/members-home.pl">Patrons</a> &rsaquo; Capture patron image</div>
12
    <h1>Koha Patron Image Taker</h1>
12
13
    <p>Ask the patron to face the webcam to take their picture.</p>
13
<div class="main container-fluid">
14
    <p>WARNING: If your machine does not have a webcam,
14
    <div class="row">
15
       your browser is not allowed camera access, your operating
15
        <div class="col-md-8 col-md-offset-2">
16
       system blocks the browser app from accessing the camera,
16
            <h1>Capture patron image</h1>
17
       or your browser does not support the javascript used
17
            <div id="webcam">
18
       in this page, this may not work and likely looks like
18
                <p>Ask the patron to face the webcam to take their picture.</p>
19
       a empty page.  If this is working you will see a video
19
20
       playing of what the webcam sees.</p>
20
                <!-- display what will be captured -->
21
21
                <video id="video" width="100%" autoplay></video><br>
22
    <!-- display what will be captured -->
22
                <!-- button to trigger cascading clicks resulting in a saved file -->
23
    <video id="video" width="640" height="480" autoplay></video><br>
23
                <!-- first stick the image into a hidden canvas below -->
24
    <!-- button to trigger cascading clicks resulting in a saved file -->
24
                <button id="snap"><i class="fa fa-camera"></i>  Take the patron's picture</button>
25
    <!-- first stick the image into a hidden canvas below -->
25
                <canvas id="canvas" width="640" height="480" style="display:none"></canvas>
26
    <button id="snap">Take the Patron's Picture</button>
26
                <!-- which will get programmatically clicked forcing the download -->
27
    <canvas id="canvas" width="640" height="480" style="display:none"></canvas>
27
                <a href="#" id="download" style="display:none">Download the patron's picture</a>
28
    <!-- then change the href in a hidden link to the picture data -->
28
            </div>
29
    <button id="save" style="display:none">Save the Patron's Picture</button>
29
        </div> <!-- /.col-md-8.col-md-offset-2 -->
30
    <!-- which will get programmatically clicked forcing the download -->
30
    </div> <!-- /.row -->
31
    <a href="#" id="download" style="display:none">Download the Patron's Picture</a>
31
</div> <!-- /.main.container-fluid -->
32
32
33
    <!-- the javascript magic that makes it happen -->
33
[% INCLUDE 'intranet-bottom.inc' %]
34
    <script>
35
      // Put event listeners into place
36
      window.addEventListener("DOMContentLoaded",
37
        function() {
38
          // Grab elements, create settings, etc.
39
          var canvas = document.getElementById('canvas');
40
          var context = canvas.getContext('2d');
41
          var video = document.getElementById('video');
42
          var mediaConfig =  { video: true };
43
          var errBack = function(e) {
44
            alert('An error has occurred!');
45
          };
46
47
          // Put video listeners into place
48
          if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
49
            navigator.mediaDevices.getUserMedia(mediaConfig).then(
50
              function(stream) {
51
                video.srcObject = stream;
52
                video.play();
53
              }
54
            );
55
          }
56
          /* Legacy code below! */
57
          else if (navigator.getUserMedia) { // Standard
58
            navigator.getUserMedia(mediaConfig,
59
              function(stream) {
60
                video.src = stream;
61
                video.play();
62
              },
63
              errBack
64
            );
65
          } else if (navigator.webkitGetUserMedia) { // WebKit-prefixed
66
            navigator.webkitGetUserMedia(mediaConfig,
67
              function(stream) {
68
                video.src = window.webkitURL.createObjectURL(stream);
69
                video.play();
70
              },
71
              errBack
72
            );
73
          } else if (navigator.mozGetUserMedia) { // Mozilla-prefixed
74
            navigator.mozGetUserMedia(mediaConfig,
75
              function(stream) {
76
                video.src = window.URL.createObjectURL(stream);
77
                video.play();
78
              },
79
              errBack
80
            );
81
          }
82
83
          // Trigger photo take
84
          document.getElementById('snap').addEventListener('click',
85
            function() {
86
              context.drawImage(video, 0, 0, 640, 480);
87
              var save_button = document.getElementById('save');
88
              save_button.click();
89
            }
90
          );
91
92
          // Save photo taken
93
          document.getElementById('save').addEventListener('click',
94
            function() {
95
              var download_anchor = document.getElementById('download');
96
              download_anchor.href = canvas.toDataURL('image/jpeg');
97
              download_anchor.download = "SamplePhoto.jpg";
98
              download_anchor.click();
99
            }
100
          );
101
        },
102
        false
103
      );
104
105
    </script>
106
  </body>
107
</html>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt (-3 / +8 lines)
Lines 15-20 Link Here
15
    [% END %]
15
    [% END %]
16
</title>
16
</title>
17
[% INCLUDE 'doc-head-close.inc' %]
17
[% INCLUDE 'doc-head-close.inc' %]
18
<style>video{width:100%;}</style>
18
</head>
19
</head>
19
20
20
<body id="pat_moremember" class="pat">
21
<body id="pat_moremember" class="pat">
Lines 297-303 Link Here
297
                                            </div>
298
                                            </div>
298
                                        [% ELSE %]
299
                                        [% ELSE %]
299
                                            <div class="patroninfo-heading">
300
                                            <div class="patroninfo-heading">
300
                                                <h3>Upload patron image</h3>
301
                                                <h3>Add patron image</h3>
301
                                                <a class="btn btn-default btn-xs" id="show-picture-upload" href="#"><i class="fa fa-plus"></i> Add</a>
302
                                                <a class="btn btn-default btn-xs" id="show-picture-upload" href="#"><i class="fa fa-plus"></i> Add</a>
302
                                            </div>
303
                                            </div>
303
                                        [% END %]
304
                                        [% END %]
Lines 323-331 Link Here
323
                                                <a href="#" id="cancel-picture-upload" class="cancel">Cancel</a>
324
                                                <a href="#" id="cancel-picture-upload" class="cancel">Cancel</a>
324
                                            </div>
325
                                            </div>
325
                                        </form>
326
                                        </form>
326
                                        <div class="patroninfo-heading" id="take_patron_image">
327
                                        <div class="patroninfo-heading" id="take_patron_image" style="display:block">
327
                                            <h3>Take patron image</h3>
328
                                            <h3>Take patron image</h3>
328
                                            <a class="btn btn-default btn-xs" id="trigger-webcam" href="/cgi-bin/koha/members/webcam.pl" target="_blank"><i class="fa fa-camera"></i> Take</a>
329
                                            <a class="btn btn-default btn-xs" id="trigger-webcam" href="/cgi-bin/koha/members/webcam.pl" title="Capture patron image"> <i class="fa fa-camera"></i> Take</a>
329
                                        </div>
330
                                        </div>
330
                                    </div> [% # /div#manage-patron-image %]
331
                                    </div> [% # /div#manage-patron-image %]
331
                                [% END %]
332
                                [% END %]
Lines 864-869 Link Here
864
    [% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %]
865
    [% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %]
865
    [% Asset.js("lib/jquery/plugins/jquery-ui-timepicker-addon.min.js") | $raw %]
866
    [% Asset.js("lib/jquery/plugins/jquery-ui-timepicker-addon.min.js") | $raw %]
866
    [% INCLUDE 'timepicker.inc' %]
867
    [% INCLUDE 'timepicker.inc' %]
868
    [% IF ( Koha.Preference('patronimages') ) %]
869
        [% Asset.js("js/pages/patron-webcam.js") %]
870
    [% END %]
867
    <script>
871
    <script>
868
        /* Set some variable needed in circulation.js */
872
        /* Set some variable needed in circulation.js */
869
        var logged_in_user_borrowernumber = "[% logged_in_user.borrowernumber | html %]";
873
        var logged_in_user_borrowernumber = "[% logged_in_user.borrowernumber | html %]";
Lines 956-961 Link Here
956
            });
960
            });
957
961
958
         });
962
         });
963
959
        function uncheck_sibling(me){
964
        function uncheck_sibling(me){
960
            nodename=me.getAttribute("name");
965
            nodename=me.getAttribute("name");
961
            if (nodename =="barcodes[]"){
966
            if (nodename =="barcodes[]"){
(-)a/koha-tmpl/intranet-tmpl/prog/js/pages/patron-webcam.js (-1 / +113 lines)
Line 0 Link Here
0
- 
1
function initializeCamera( camera_form ){
2
    // Grab elements, create settings, etc.
3
    console.log(camera_form);
4
    var context = camera_form.find("#canvas")[0].getContext('2d');
5
    var video = camera_form.find("#video")[0];
6
    var mediaConfig =  { video: true };
7
    var errBack = function( error ) {
8
        if( error.name = "NotAllowedError" ){
9
            alert("No permission to use camera");
10
        } else {
11
            alert("Cannot load camera interface");
12
        }
13
    };
14
15
    // Put video listeners into place
16
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
17
        navigator.mediaDevices.getUserMedia(mediaConfig).then(
18
            function(stream) {
19
                video.srcObject = stream;
20
                video.play();
21
            },
22
            errBack
23
        );
24
    }
25
    /* Legacy code below! */
26
    else if (navigator.getUserMedia) { // Standard
27
        navigator.getUserMedia(mediaConfig,
28
            function(stream) {
29
                video.src = stream;
30
                video.play();
31
            },
32
            errBack
33
        );
34
    } else if (navigator.webkitGetUserMedia) { // WebKit-prefixed
35
        navigator.webkitGetUserMedia(mediaConfig,
36
            function(stream) {
37
                video.src = window.webkitURL.createObjectURL(stream);
38
                video.play();
39
            },
40
            errBack
41
        );
42
    } else if (navigator.mozGetUserMedia) { // Mozilla-prefixed
43
        navigator.mozGetUserMedia(mediaConfig,
44
            function(stream) {
45
                video.src = window.URL.createObjectURL(stream);
46
                video.play();
47
            },
48
            errBack
49
        );
50
    }
51
52
    camera_form.find("#snap").on("click", function(){
53
        context.drawImage(video, 0, 0, 640, 480);
54
        var download_anchor = document.getElementById('download');
55
        download_anchor.href = canvas.toDataURL('image/jpeg');
56
        download_anchor.download = "patron-photo.jpg";
57
        download_anchor.click();
58
        // gather form data for AJAX POST
59
        var data = new FormData();
60
        data.append("uploadfiletext", download_anchor.href );
61
        data.append("csrf_token", $("#webcam_csrf_token").val() );
62
        data.append("filetype", "image" );
63
        data.append("uploadfilename", "patron-photo.jpg" );
64
        data.append("cardnumber", $("#webcam_cardnumber").val() );
65
        data.append("borrowernumber", $("#webcam_borrowernumber").val() );
66
        data.append("op", $("#webcam_op").val() );
67
        $.ajax({
68
            type: "POST",
69
            url: "/cgi-bin/koha/tools/picture-upload.pl",
70
            data: data,
71
            cache: false,
72
            contentType: false,
73
            processData: false
74
        }).done(function() {
75
76
        });
77
    });
78
}
79
80
function detectWebcam(callback) {
81
    let md = navigator.mediaDevices;
82
    if (!md || !md.enumerateDevices) return callback(false);
83
    md.enumerateDevices().then(devices => {
84
        callback(devices.some(device => 'videoinput' === device.kind));
85
    })
86
}
87
88
detectWebcam(function(hasWebcam) {
89
    let div_take_image = document.getElementById('take_patron_image');
90
    if (div_take_image) {
91
        div_take_image.style.display = hasWebcam ? "block" : "none";
92
    }
93
});
94
95
$(document).ready(function(){
96
    $("#trigger-webcam").on("click", function(e){
97
        e.preventDefault();
98
        var borrowernumber = $(this).data("borrowernumber");
99
        $.get("/cgi-bin/koha/members/webcam.pl", { borrowernumber : borrowernumber }, function( data ){
100
            var image_form = $(data).find("#webcam");
101
            initializeCamera( image_form );
102
            $("#patronImageEdit .modal-body").html( image_form );
103
        });
104
        var modalTitle = $(this).attr("title");
105
        $("#patronImageEdit .modal-title").text(modalTitle);
106
        $("#patronImageEdit")
107
            .modal("show")
108
            .on("hidden.bs.modal", function(){
109
                video.pause();
110
            });
111
    });
112
113
})

Return to bug 6815