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 159-164 Link Here
159
            <div class="modal-body">
159
            <div class="modal-body">
160
                <img src="[% interface | html %]/[% theme | html %]/img/spinner-small.gif" alt="" />
160
                <img src="[% interface | html %]/[% theme | html %]/img/spinner-small.gif" alt="" />
161
            </div>
161
            </div>
162
            <form method="post" action="/cgi-bin/koha/tools/picture-upload.pl" enctype="multipart/form-data">
163
                <input type="hidden" id="webcam_csrf_token" name="csrf_token" value="[% csrf_token | html %]" />
164
                <input type="hidden" id="webcam_image" name="filetype" value="image" />
165
                <input type="hidden" id="webcam_cardnumber" name="cardnumber" value="[% patron.cardnumber | html %]" />
166
                <input type="hidden" id="webcam_borrowernumber" name="borrowernumber" value="[% patron.borrowernumber | html %]" />
167
                <input id="webcam_op" name="op" type="hidden" value="Upload" />
168
            </form>
162
            <div class="modal-footer">
169
            <div class="modal-footer">
163
                <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
170
                <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
164
            </div>
171
            </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 17-22 Link Here
17
    [% END %]
17
    [% END %]
18
</title>
18
</title>
19
[% INCLUDE 'doc-head-close.inc' %]
19
[% INCLUDE 'doc-head-close.inc' %]
20
<style>video{width:100%;}</style>
20
</head>
21
</head>
21
22
22
<body id="pat_moremember" class="pat">
23
<body id="pat_moremember" class="pat">
Lines 306-312 Link Here
306
                                            </div>
307
                                            </div>
307
                                        [% ELSE %]
308
                                        [% ELSE %]
308
                                            <div class="patroninfo-heading">
309
                                            <div class="patroninfo-heading">
309
                                                <h3>Upload patron image</h3>
310
                                                <h3>Add patron image</h3>
310
                                                <a class="btn btn-default btn-xs" id="show-picture-upload" href="#"><i class="fa fa-plus"></i> Add</a>
311
                                                <a class="btn btn-default btn-xs" id="show-picture-upload" href="#"><i class="fa fa-plus"></i> Add</a>
311
                                            </div>
312
                                            </div>
312
                                        [% END %]
313
                                        [% END %]
Lines 332-340 Link Here
332
                                                <a href="#" id="cancel-picture-upload" class="cancel">Cancel</a>
333
                                                <a href="#" id="cancel-picture-upload" class="cancel">Cancel</a>
333
                                            </div>
334
                                            </div>
334
                                        </form>
335
                                        </form>
335
                                        <div class="patroninfo-heading" id="take_patron_image">
336
                                        <div class="patroninfo-heading" id="take_patron_image" style="display:block">
336
                                            <h3>Take patron image</h3>
337
                                            <h3>Take patron image</h3>
337
                                            <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>
338
                                            <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>
338
                                        </div>
339
                                        </div>
339
                                    </div> [% # /div#manage-patron-image %]
340
                                    </div> [% # /div#manage-patron-image %]
340
                                [% END %]
341
                                [% END %]
Lines 879-884 Link Here
879
    [% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %]
880
    [% Asset.js("lib/jquery/plugins/jquery.checkboxes.min.js") | $raw %]
880
    [% Asset.js("lib/jquery/plugins/jquery-ui-timepicker-addon.min.js") | $raw %]
881
    [% Asset.js("lib/jquery/plugins/jquery-ui-timepicker-addon.min.js") | $raw %]
881
    [% INCLUDE 'timepicker.inc' %]
882
    [% INCLUDE 'timepicker.inc' %]
883
    [% IF ( Koha.Preference('patronimages') ) %]
884
        [% Asset.js("js/pages/patron-webcam.js") %]
885
    [% END %]
882
    <script>
886
    <script>
883
        /* Set some variable needed in circulation.js */
887
        /* Set some variable needed in circulation.js */
884
        var logged_in_user_borrowernumber = "[% logged_in_user.borrowernumber | html %]";
888
        var logged_in_user_borrowernumber = "[% logged_in_user.borrowernumber | html %]";
Lines 963-968 Link Here
963
            });
967
            });
964
968
965
         });
969
         });
970
966
        function uncheck_sibling(me){
971
        function uncheck_sibling(me){
967
            nodename=me.getAttribute("name");
972
            nodename=me.getAttribute("name");
968
            if (nodename =="barcodes[]"){
973
            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