src/ApplicationBundle/Modules/System/Resources/views/inc/_signature_setup_modal.html.twig line 1

Open in your IDE?
  1. {#
  2.   First-run signature/hash onboarding modal (TODO-A, see APPROVAL_SYSTEM_AUDIT.md §2).
  3.   Included once from the global ERP footer. On first page load per session it asks
  4.   /signature_status; if the user has no signature yet it pops this modal so they set
  5.   their signature + approval hash before hitting an approval action and getting stuck.
  6.   Posts to the same update_signature endpoint (with redirectTo so they land back here).
  7.   Purely additive — fails safe if the status call errors.
  8. #}
  9. <div class="modal fade" id="hbSignatureSetupModal" tabindex="-1" role="dialog" aria-labelledby="hbSigSetupTitle">
  10.   <div class="modal-dialog" role="document" style="max-width:520px;">
  11.     <div class="modal-content">
  12.       <form role="form" method="post" action="{{ path('update_signature') }}"
  13.             enctype="multipart/form-data" id="hbSigSetupForm">
  14.         <input type="hidden" name="redirectTo" id="hbSigRedirectTo" value="">
  15.         <div class="modal-header" style="background:#5e35b1;color:#fff;border-radius:6px 6px 0 0;">
  16.           <h4 class="modal-title" id="hbSigSetupTitle" style="color:#fff;">
  17.             <i class="fa fa-pencil-square-o"></i> Set or update your approval signature
  18.           </h4>
  19.         </div>
  20.         <div class="modal-body">
  21.           <p style="color:#555;margin-bottom:18px;">
  22.             Before you can approve documents you need a one-time digital signature and a
  23.             secret <b>approval hash</b>. Your signature image is encrypted with this hash —
  24.             <b>memorise it and never share it</b>.
  25.           </p>
  26.           <div id="hbSigSetupError" class="text-danger" style="margin-bottom:12px;"></div>
  27.           <div class="form-group">
  28.             <label for="hbSigHash">Digital Encryption Hash</label>
  29.             <input type="password" required class="form-control" id="hbSigHash"
  30.                    autocomplete="off" name="approvalHash" placeholder="Enter a secret code">
  31.           </div>
  32.           <div class="form-group">
  33.             <label for="hbSigHashAgain">Re-enter Encryption Hash</label>
  34.             <input type="password" required class="form-control" id="hbSigHashAgain"
  35.                    autocomplete="off" name="approvalHashAgain" placeholder="Retype the code">
  36.           </div>
  37.           <div class="form-group">
  38.             <label for="hbSigFile" style="display:block;">Upload Signature <small style="color:#888;">(image, width &gt; height, &lt; 200 kB)</small></label>
  39.             <input type="file" name="myfile" accept="image/*" id="hbSigFile"
  40.                    style="display:block;width:100%;padding:8px;border:1px solid #ced4da;border-radius:6px;background:#fff;">
  41.           </div>
  42.         </div>
  43.         <div class="modal-footer">
  44.           <button type="button" class="btn btn-default" data-dismiss="modal">Later</button>
  45.           <button type="submit" class="btn btn-success"><i class="fa fa-check"></i> Save signature</button>
  46.         </div>
  47.       </form>
  48.     </div>
  49.   </div>
  50. </div>
  51. <script>
  52. (function () {
  53.   if (typeof jQuery === 'undefined') return;
  54.   var $ = jQuery;
  55.   // Show/hide that works WITH or WITHOUT Bootstrap's modal plugin. The cp-shell
  56.   // module pages (Approval & Access etc.) load jQuery but not the codecovers
  57.   // Bootstrap JS bundle, so $.fn.modal is undefined there — fall back to a
  58.   // manual overlay (uses Bootstrap CSS if present, plus inline styles to be safe).
  59.   function hbShowSig() {
  60.     var $m = $('#hbSignatureSetupModal');
  61.     if ($.fn && typeof $.fn.modal === 'function') { $m.modal({ backdrop: 'static', keyboard: true }); return; }
  62.     if (!$('#hbSigBackdrop').length) {
  63.       $('<div id="hbSigBackdrop"></div>').css({
  64.         position: 'fixed', top: 0, left: 0, right: 0, bottom: 0,
  65.         background: 'rgba(0,0,0,.5)', zIndex: 1040
  66.       }).appendTo('body');
  67.     }
  68.     $m.addClass('in').css({ display: 'block', position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, zIndex: 1050, overflow: 'auto' });
  69.     $('body').addClass('modal-open');
  70.   }
  71.   function hbHideSig() {
  72.     var $m = $('#hbSignatureSetupModal');
  73.     if ($.fn && typeof $.fn.modal === 'function') { $m.modal('hide'); }
  74.     else { $m.removeClass('in').css('display', 'none'); }
  75.     $('#hbSigBackdrop').remove();
  76.     $('body').removeClass('modal-open');
  77.   }
  78.   // Manual close (Bootstrap's data-dismiss won't fire without the plugin).
  79.   $(document).on('click', '#hbSignatureSetupModal [data-dismiss="modal"], #hbSigBackdrop', function () { hbHideSig(); });
  80.   // Client-side validation mirroring the dedicated page.
  81.   $('#hbSigSetupForm').on('submit', function (event) {
  82.     event.preventDefault();
  83.     $('#hbSigSetupError').text('');
  84.     var hash = $('#hbSigHash').val(), hashAgain = $('#hbSigHashAgain').val();
  85.     if (!hash) { $('#hbSigSetupError').text('Please enter a hash.'); return; }
  86.     if (hash !== hashAgain) { $('#hbSigSetupError').text('Hashes did not match.'); return; }
  87.     var files = $('#hbSigFile')[0].files;
  88.     if (!files.length) { $('#hbSigSetupError').text('Please select a signature image.'); return; }
  89.     var file = files[0];
  90.     if (((file.size / 1024) / 1024) > 0.2) { $('#hbSigSetupError').text('File must be under 200 kB.'); return; }
  91.     var img = new Image();
  92.     img.onload = function () {
  93.       if (img.width <= img.height) { $('#hbSigSetupError').text('Width should be greater than height.'); }
  94.       else { $('#hbSigSetupForm')[0].submit(); }
  95.     };
  96.     img.onerror = function () { $('#hbSigSetupError').text('Invalid image file.'); };
  97.     var reader = new FileReader();
  98.     reader.onload = function (e) { img.src = e.target.result; };
  99.     reader.readAsDataURL(file);
  100.   });
  101.   // Manual trigger: any element with [data-open-signature-modal] opens the modal
  102.   // (to set OR update the signature/hash on demand, not just first-run).
  103.   $(document).on('click', '[data-open-signature-modal]', function (e) {
  104.     e.preventDefault();
  105.     $('#hbSigRedirectTo').val(window.location.href);
  106.     hbShowSig();
  107.   });
  108.   // First-run check — once per session, skip the signature page itself.
  109.   $(function () {
  110.     try {
  111.       if (sessionStorage.getItem('hb_sig_checked') === '1') return;
  112.       if (window.location.pathname.indexOf('update_signature') !== -1) return;
  113.       $.getJSON('{{ path('signature_status') }}')
  114.         .done(function (res) {
  115.           sessionStorage.setItem('hb_sig_checked', '1');
  116.           if (res && res.hasSignature === false) {
  117.             $('#hbSigRedirectTo').val(window.location.href);
  118.             hbShowSig();
  119.           }
  120.         })
  121.         .fail(function () { /* fail safe: never block the page */ });
  122.     } catch (e) { /* no-op */ }
  123.   });
  124. }());
  125. </script>