diff --git a/app/Http/Controllers/ActionlogController.php b/app/Http/Controllers/ActionlogController.php new file mode 100644 index 000000000..397c670f3 --- /dev/null +++ b/app/Http/Controllers/ActionlogController.php @@ -0,0 +1,18 @@ +header('Content-Type', $filetype); + } +} diff --git a/app/Http/Controllers/ReportsController.php b/app/Http/Controllers/ReportsController.php index ada1231bf..49c5da4b7 100644 --- a/app/Http/Controllers/ReportsController.php +++ b/app/Http/Controllers/ReportsController.php @@ -356,8 +356,10 @@ class ReportsController extends Controller $activity_item = ''.e($activity->item->asset_tag).' - '. e($activity->item->showAssetName()).''; $item_type = 'asset'; } elseif ($activity->item) { - $activity_item = ''.e($activity->item->name).''; + $activity_item = '' . e($activity->item->name) . ''; $item_type = $activity->itemType(); + } else { $activity_item = "unkonwn"; $item_type = "null"; @@ -378,6 +380,9 @@ class ReportsController extends Controller } else { $activity_target = ''; } + } elseif (($activity->action_type=='accepted') || ($activity->action_type=='declined')) { + $activity_target = '' . e($activity->item->assigneduser->fullName()) . ''; + } elseif ($activity->action_type=='requested') { if ($activity->user) { $activity_target = ''.$activity->user->fullName().''; diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index 240ec58b4..77f191f60 100755 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -349,6 +349,7 @@ class SettingsController extends Controller $setting->email_domain = e(Input::get('email_domain')); $setting->email_format = e(Input::get('email_format')); $setting->username_format = e(Input::get('username_format')); + $setting->require_accept_signature = e(Input::get('require_accept_signature')); $setting->labels_per_page = e(Input::get('labels_per_page')); diff --git a/app/Http/Controllers/ViewAssetsController.php b/app/Http/Controllers/ViewAssetsController.php index b4118fa78..ddb3fa1a6 100755 --- a/app/Http/Controllers/ViewAssetsController.php +++ b/app/Http/Controllers/ViewAssetsController.php @@ -22,6 +22,7 @@ use Redirect; use Slack; use Validator; use View; +use Illuminate\Http\Request; /** * This controller handles all actions related to the ability for users @@ -294,10 +295,14 @@ class ViewAssetsController extends Controller //return redirect()->to('account')->with('error', trans('admin/hardware/message.does_not_exist')); } + if ($findlog->accepted_id!='') { + return redirect()->to('account/view-assets')->with('error', trans('admin/users/message.error.asset_already_accepted')); + } $user = Auth::user(); - if ($user->id != $findlog->checkedout_to) { + + if ($user->id != $findlog->item->assigned_to) { return redirect()->to('account/view-assets')->with('error', trans('admin/users/message.error.incorrect_user_accepted')); } @@ -310,12 +315,12 @@ class ViewAssetsController extends Controller } elseif (!Company::isCurrentUserHasAccess($item)) { return redirect()->route('requestable-assets')->with('error', trans('general.insufficient_permissions')); } else { - return View::make('account/accept-asset', compact('item'))->with('findlog', $findlog); + return View::make('account/accept-asset', compact('item'))->with('findlog', $findlog)->with('item',$item); } } // Save the acceptance - public function postAcceptAsset($logID = null) + public function postAcceptAsset(Request $request, $logID = null) { // Check if the asset exists @@ -331,15 +336,25 @@ class ViewAssetsController extends Controller } if (!Input::has('asset_acceptance')) { - return redirect()->to('account/view-assets')->with('error', trans('admin/users/message.error.accept_or_decline')); + return redirect()->back()->with('error', trans('admin/users/message.error.accept_or_decline')); } $user = Auth::user(); - if ($user->id != $findlog->checkedout_to) { + if ($user->id != $findlog->item->assigned_to) { return redirect()->to('account/view-assets')->with('error', trans('admin/users/message.error.incorrect_user_accepted')); } + if ($request->has('signature_output')) { + $path = config('app.private_uploads').'/signatures'; + $sig_filename = "siglog-".$findlog->id.'-'.date('Y-m-d-his').".png"; + $data_uri = e($request->get('signature_output')); + $encoded_image = explode(",", $data_uri); + $decoded_image = base64_decode($encoded_image[1]); + file_put_contents($path."/".$sig_filename, $decoded_image); + } + + $logaction = new Actionlog(); if (Input::get('asset_acceptance')=='accepted') { @@ -353,6 +368,7 @@ class ViewAssetsController extends Controller } $logaction->item_id = $findlog->item_id; $logaction->item_type = $findlog->item_type; + // Asset if (($findlog->item_id!='') && ($findlog->item_type==Asset::class)) { if (Input::get('asset_acceptance')!='accepted') { @@ -361,19 +377,23 @@ class ViewAssetsController extends Controller ->update(array('assigned_to' => null)); } } - $logaction->target_id = $findlog->target_id; + $logaction->target_id = $findlog->target_id; $logaction->note = e(Input::get('note')); - $logaction->user_id = $user->id; - $logaction->accepted_at = date("Y-m-d H:i:s"); + $logaction->updated_at = date("Y-m-d H:i:s"); + + + if ($sig_filename) { + $logaction->accept_signature = $sig_filename; + } $log = $logaction->logaction($logaction_msg); $update_checkout = DB::table('action_logs') ->where('id', $findlog->id) ->update(array('accepted_id' => $logaction->id)); - $affected_asset=$logaction->assetlog; - $affected_asset->accepted=$accepted; + $affected_asset = $logaction->item; + $affected_asset->accepted = $accepted; $affected_asset->save(); if ($update_checkout) { diff --git a/app/Http/routes.php b/app/Http/routes.php index c8cf02d2a..19c19b96e 100755 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -379,6 +379,30 @@ Route::group( } ); +/* +|-------------------------------------------------------------------------- +| Log Routes +|-------------------------------------------------------------------------- +| +| Register all the admin routes. +| +*/ + +Route::group(['middleware' => 'auth'], function () { + + Route::get( + 'display-sig/{filename}', + [ + 'as' => 'log.signature.view', + 'middleware' => 'authorize:assets.view', + 'uses' => 'ActionlogController@displaySig' ] + ); + + +}); + + + /* |-------------------------------------------------------------------------- | Admin Routes diff --git a/app/Models/Actionlog.php b/app/Models/Actionlog.php index 613df7585..21a1cb75d 100755 --- a/app/Models/Actionlog.php +++ b/app/Models/Actionlog.php @@ -4,8 +4,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Log; +use Response; /** * Model for the Actionlog (the table that keeps a historical log of @@ -91,15 +90,14 @@ class Actionlog extends Model /** * Check if the file exists, and if it does, force a download **/ - public function get_src($type = 'assets') + public function get_src($type = 'assets', $fieldname = 'filename') { - - $file = config('app.private_uploads') . '/' . $type . '/' . $this->filename; - + $file = config('app.private_uploads') . '/' . $type . '/' . $this->{$fieldname}; return $file; - } + + /** * Get the parent category name */ diff --git a/database/migrations/2016_10_29_082408_add_signature_to_acceptance.php b/database/migrations/2016_10_29_082408_add_signature_to_acceptance.php new file mode 100644 index 000000000..c29092ec7 --- /dev/null +++ b/database/migrations/2016_10_29_082408_add_signature_to_acceptance.php @@ -0,0 +1,39 @@ +boolean('require_accept_signature')->default(0); + }); + + Schema::table('action_logs', function ($table) { + $table->string('accept_signature', 100)->nullable()->default(null); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('settings', function ($table) { + $table->dropColumn('require_accept_signature'); + }); + + Schema::table('action_logs', function ($table) { + $table->dropColumn('accept_signature'); + }); + } +} diff --git a/public/assets/css/signature-pad.css b/public/assets/css/signature-pad.css new file mode 100755 index 000000000..97e68714b --- /dev/null +++ b/public/assets/css/signature-pad.css @@ -0,0 +1,136 @@ + +#signature-pad { + padding-top: 250px; + margin: auto; +} +.m-signature-pad { + + position: relative; + font-size: 10px; + width: 100%; + height: 300px; + border: 1px solid #e8e8e8; + background-color: #fff; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.27), 0 0 40px rgba(0, 0, 0, 0.08) inset; + border-radius: 4px; +} + +.m-signature-pad:before, .m-signature-pad:after { + position: absolute; + z-index: -1; + content: ""; + width: 40%; + height: 10px; + left: 20px; + bottom: 10px; + background: transparent; + -webkit-transform: skew(-3deg) rotate(-3deg); + -moz-transform: skew(-3deg) rotate(-3deg); + -ms-transform: skew(-3deg) rotate(-3deg); + -o-transform: skew(-3deg) rotate(-3deg); + transform: skew(-3deg) rotate(-3deg); + box-shadow: 0 8px 12px rgba(0, 0, 0, 0.4); +} + +.m-signature-pad:after { + left: auto; + right: 20px; + -webkit-transform: skew(3deg) rotate(3deg); + -moz-transform: skew(3deg) rotate(3deg); + -ms-transform: skew(3deg) rotate(3deg); + -o-transform: skew(3deg) rotate(3deg); + transform: skew(3deg) rotate(3deg); +} + +.m-signature-pad--body { + position: absolute; + left: 20px; + right: 20px; + top: 20px; + bottom: 60px; + border: 1px solid #f4f4f4; +} + +.m-signature-pad--body + canvas { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + border-radius: 4px; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.02) inset; + } + +.m-signature-pad--footer { + position: absolute; + left: 20px; + right: 20px; + bottom: 20px; + height: 40px; +} + +.m-signature-pad--footer + .description { + color: #C3C3C3; + text-align: center; + font-size: 1.2em; + margin-top: 1.8em; + } + +.m-signature-pad--footer + .button { + position: absolute; + bottom: 0; + } + +.m-signature-pad--footer + .button.clear { + left: 0; + } + +.m-signature-pad--footer + .button.save { + right: 0; + } + +@media screen and (max-width: 1024px) { + .m-signature-pad { + top: 0; + left: 0; + right: 0; + bottom: 0; + width: auto; + height: auto; + min-width: 250px; + min-height: 140px; + margin: 5%; + } + +} + +@media screen and (min-device-width: 768px) and (max-device-width: 1024px) { + .m-signature-pad { + margin: 10%; + } +} + +@media screen and (max-height: 320px) { + .m-signature-pad--body { + left: 0; + right: 0; + top: 0; + bottom: 32px; + } + .m-signature-pad--footer { + left: 20px; + right: 20px; + bottom: 4px; + height: 28px; + } + .m-signature-pad--footer + .description { + font-size: 1em; + margin-top: 1em; + } +} diff --git a/public/assets/js/signature_pad.js b/public/assets/js/signature_pad.js new file mode 100755 index 000000000..d2b04c6a8 --- /dev/null +++ b/public/assets/js/signature_pad.js @@ -0,0 +1,389 @@ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module unless amdModuleId is set + define([], function () { + return (root['SignaturePad'] = factory()); + }); + } else if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); + } else { + root['SignaturePad'] = factory(); + } +}(this, function () { + +/*! + * Signature Pad v1.5.3 + * https://github.com/szimek/signature_pad + * + * Copyright 2016 Szymon Nowak + * Released under the MIT license + * + * The main idea and some parts of the code (e.g. drawing variable width Bézier curve) are taken from: + * http://corner.squareup.com/2012/07/smoother-signatures.html + * + * Implementation of interpolation using cubic Bézier curves is taken from: + * http://benknowscode.wordpress.com/2012/09/14/path-interpolation-using-cubic-bezier-and-control-point-estimation-in-javascript + * + * Algorithm for approximated length of a Bézier curve is taken from: + * http://www.lemoda.net/maths/bezier-length/index.html + * + */ +var SignaturePad = (function (document) { + "use strict"; + + var SignaturePad = function (canvas, options) { + var self = this, + opts = options || {}; + + this.velocityFilterWeight = opts.velocityFilterWeight || 0.7; + this.minWidth = opts.minWidth || 0.5; + this.maxWidth = opts.maxWidth || 2.5; + this.dotSize = opts.dotSize || function () { + return (this.minWidth + this.maxWidth) / 2; + }; + this.penColor = opts.penColor || "black"; + this.backgroundColor = opts.backgroundColor || "rgba(0,0,0,0)"; + this.onEnd = opts.onEnd; + this.onBegin = opts.onBegin; + + this._canvas = canvas; + this._ctx = canvas.getContext("2d"); + this.clear(); + + // we need add these inline so they are available to unbind while still having + // access to 'self' we could use _.bind but it's not worth adding a dependency + this._handleMouseDown = function (event) { + if (event.which === 1) { + self._mouseButtonDown = true; + self._strokeBegin(event); + } + }; + + this._handleMouseMove = function (event) { + if (self._mouseButtonDown) { + self._strokeUpdate(event); + } + }; + + this._handleMouseUp = function (event) { + if (event.which === 1 && self._mouseButtonDown) { + self._mouseButtonDown = false; + self._strokeEnd(event); + } + }; + + this._handleTouchStart = function (event) { + if (event.targetTouches.length == 1) { + var touch = event.changedTouches[0]; + self._strokeBegin(touch); + } + }; + + this._handleTouchMove = function (event) { + // Prevent scrolling. + event.preventDefault(); + + var touch = event.targetTouches[0]; + self._strokeUpdate(touch); + }; + + this._handleTouchEnd = function (event) { + var wasCanvasTouched = event.target === self._canvas; + if (wasCanvasTouched) { + event.preventDefault(); + self._strokeEnd(event); + } + }; + + this._handleMouseEvents(); + this._handleTouchEvents(); + }; + + SignaturePad.prototype.clear = function () { + var ctx = this._ctx, + canvas = this._canvas; + + ctx.fillStyle = this.backgroundColor; + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.fillRect(0, 0, canvas.width, canvas.height); + this._reset(); + }; + + SignaturePad.prototype.toDataURL = function (imageType, quality) { + var canvas = this._canvas; + return canvas.toDataURL.apply(canvas, arguments); + }; + + SignaturePad.prototype.fromDataURL = function (dataUrl) { + var self = this, + image = new Image(), + ratio = window.devicePixelRatio || 1, + width = this._canvas.width / ratio, + height = this._canvas.height / ratio; + + this._reset(); + image.src = dataUrl; + image.onload = function () { + self._ctx.drawImage(image, 0, 0, width, height); + }; + this._isEmpty = false; + }; + + SignaturePad.prototype._strokeUpdate = function (event) { + var point = this._createPoint(event); + this._addPoint(point); + }; + + SignaturePad.prototype._strokeBegin = function (event) { + this._reset(); + this._strokeUpdate(event); + if (typeof this.onBegin === 'function') { + this.onBegin(event); + } + }; + + SignaturePad.prototype._strokeDraw = function (point) { + var ctx = this._ctx, + dotSize = typeof(this.dotSize) === 'function' ? this.dotSize() : this.dotSize; + + ctx.beginPath(); + this._drawPoint(point.x, point.y, dotSize); + ctx.closePath(); + ctx.fill(); + }; + + SignaturePad.prototype._strokeEnd = function (event) { + var canDrawCurve = this.points.length > 2, + point = this.points[0]; + + if (!canDrawCurve && point) { + this._strokeDraw(point); + } + if (typeof this.onEnd === 'function') { + this.onEnd(event); + } + }; + + SignaturePad.prototype._handleMouseEvents = function () { + this._mouseButtonDown = false; + + this._canvas.addEventListener("mousedown", this._handleMouseDown); + this._canvas.addEventListener("mousemove", this._handleMouseMove); + document.addEventListener("mouseup", this._handleMouseUp); + }; + + SignaturePad.prototype._handleTouchEvents = function () { + // Pass touch events to canvas element on mobile IE11 and Edge. + this._canvas.style.msTouchAction = 'none'; + this._canvas.style.touchAction = 'none'; + + this._canvas.addEventListener("touchstart", this._handleTouchStart); + this._canvas.addEventListener("touchmove", this._handleTouchMove); + this._canvas.addEventListener("touchend", this._handleTouchEnd); + }; + + SignaturePad.prototype.on = function () { + this._handleMouseEvents(); + this._handleTouchEvents(); + }; + + SignaturePad.prototype.off = function () { + this._canvas.removeEventListener("mousedown", this._handleMouseDown); + this._canvas.removeEventListener("mousemove", this._handleMouseMove); + document.removeEventListener("mouseup", this._handleMouseUp); + + this._canvas.removeEventListener("touchstart", this._handleTouchStart); + this._canvas.removeEventListener("touchmove", this._handleTouchMove); + this._canvas.removeEventListener("touchend", this._handleTouchEnd); + }; + + SignaturePad.prototype.isEmpty = function () { + return this._isEmpty; + }; + + SignaturePad.prototype._reset = function () { + this.points = []; + this._lastVelocity = 0; + this._lastWidth = (this.minWidth + this.maxWidth) / 2; + this._isEmpty = true; + this._ctx.fillStyle = this.penColor; + }; + + SignaturePad.prototype._createPoint = function (event) { + var rect = this._canvas.getBoundingClientRect(); + return new Point( + event.clientX - rect.left, + event.clientY - rect.top + ); + }; + + SignaturePad.prototype._addPoint = function (point) { + var points = this.points, + c2, c3, + curve, tmp; + + points.push(point); + + if (points.length > 2) { + // To reduce the initial lag make it work with 3 points + // by copying the first point to the beginning. + if (points.length === 3) points.unshift(points[0]); + + tmp = this._calculateCurveControlPoints(points[0], points[1], points[2]); + c2 = tmp.c2; + tmp = this._calculateCurveControlPoints(points[1], points[2], points[3]); + c3 = tmp.c1; + curve = new Bezier(points[1], c2, c3, points[2]); + this._addCurve(curve); + + // Remove the first element from the list, + // so that we always have no more than 4 points in points array. + points.shift(); + } + }; + + SignaturePad.prototype._calculateCurveControlPoints = function (s1, s2, s3) { + var dx1 = s1.x - s2.x, dy1 = s1.y - s2.y, + dx2 = s2.x - s3.x, dy2 = s2.y - s3.y, + + m1 = {x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0}, + m2 = {x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0}, + + l1 = Math.sqrt(dx1*dx1 + dy1*dy1), + l2 = Math.sqrt(dx2*dx2 + dy2*dy2), + + dxm = (m1.x - m2.x), + dym = (m1.y - m2.y), + + k = l2 / (l1 + l2), + cm = {x: m2.x + dxm*k, y: m2.y + dym*k}, + + tx = s2.x - cm.x, + ty = s2.y - cm.y; + + return { + c1: new Point(m1.x + tx, m1.y + ty), + c2: new Point(m2.x + tx, m2.y + ty) + }; + }; + + SignaturePad.prototype._addCurve = function (curve) { + var startPoint = curve.startPoint, + endPoint = curve.endPoint, + velocity, newWidth; + + velocity = endPoint.velocityFrom(startPoint); + velocity = this.velocityFilterWeight * velocity + + (1 - this.velocityFilterWeight) * this._lastVelocity; + + newWidth = this._strokeWidth(velocity); + this._drawCurve(curve, this._lastWidth, newWidth); + + this._lastVelocity = velocity; + this._lastWidth = newWidth; + }; + + SignaturePad.prototype._drawPoint = function (x, y, size) { + var ctx = this._ctx; + + ctx.moveTo(x, y); + ctx.arc(x, y, size, 0, 2 * Math.PI, false); + this._isEmpty = false; + }; + + SignaturePad.prototype._drawCurve = function (curve, startWidth, endWidth) { + var ctx = this._ctx, + widthDelta = endWidth - startWidth, + drawSteps, width, i, t, tt, ttt, u, uu, uuu, x, y; + + drawSteps = Math.floor(curve.length()); + ctx.beginPath(); + for (i = 0; i < drawSteps; i++) { + // Calculate the Bezier (x, y) coordinate for this step. + t = i / drawSteps; + tt = t * t; + ttt = tt * t; + u = 1 - t; + uu = u * u; + uuu = uu * u; + + x = uuu * curve.startPoint.x; + x += 3 * uu * t * curve.control1.x; + x += 3 * u * tt * curve.control2.x; + x += ttt * curve.endPoint.x; + + y = uuu * curve.startPoint.y; + y += 3 * uu * t * curve.control1.y; + y += 3 * u * tt * curve.control2.y; + y += ttt * curve.endPoint.y; + + width = startWidth + ttt * widthDelta; + this._drawPoint(x, y, width); + } + ctx.closePath(); + ctx.fill(); + }; + + SignaturePad.prototype._strokeWidth = function (velocity) { + return Math.max(this.maxWidth / (velocity + 1), this.minWidth); + }; + + + var Point = function (x, y, time) { + this.x = x; + this.y = y; + this.time = time || new Date().getTime(); + }; + + Point.prototype.velocityFrom = function (start) { + return (this.time !== start.time) ? this.distanceTo(start) / (this.time - start.time) : 1; + }; + + Point.prototype.distanceTo = function (start) { + return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2)); + }; + + var Bezier = function (startPoint, control1, control2, endPoint) { + this.startPoint = startPoint; + this.control1 = control1; + this.control2 = control2; + this.endPoint = endPoint; + }; + + // Returns approximated length. + Bezier.prototype.length = function () { + var steps = 10, + length = 0, + i, t, cx, cy, px, py, xdiff, ydiff; + + for (i = 0; i <= steps; i++) { + t = i / steps; + cx = this._point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x); + cy = this._point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y); + if (i > 0) { + xdiff = cx - px; + ydiff = cy - py; + length += Math.sqrt(xdiff * xdiff + ydiff * ydiff); + } + px = cx; + py = cy; + } + return length; + }; + + Bezier.prototype._point = function (t, start, c1, c2, end) { + return start * (1.0 - t) * (1.0 - t) * (1.0 - t) + + 3.0 * c1 * (1.0 - t) * (1.0 - t) * t + + 3.0 * c2 * (1.0 - t) * t * t + + end * t * t * t; + }; + + return SignaturePad; +})(document); + +return SignaturePad; + +})); diff --git a/public/assets/js/signature_pad.min.js b/public/assets/js/signature_pad.min.js new file mode 100755 index 000000000..42fa23d0f --- /dev/null +++ b/public/assets/js/signature_pad.min.js @@ -0,0 +1,5 @@ +!function(a,b){"function"==typeof define&&define.amd?define([],function(){return a.SignaturePad=b()}):"object"==typeof exports?module.exports=b():a.SignaturePad=b()}(this,function(){/*! + * Signature Pad v1.5.3 | https://github.com/szimek/signature_pad + * (c) 2016 Szymon Nowak | Released under the MIT license + */ +var a=function(a){"use strict";var b=function(a,b){var c=this,d=b||{};this.velocityFilterWeight=d.velocityFilterWeight||.7,this.minWidth=d.minWidth||.5,this.maxWidth=d.maxWidth||2.5,this.dotSize=d.dotSize||function(){return(this.minWidth+this.maxWidth)/2},this.penColor=d.penColor||"black",this.backgroundColor=d.backgroundColor||"rgba(0,0,0,0)",this.onEnd=d.onEnd,this.onBegin=d.onBegin,this._canvas=a,this._ctx=a.getContext("2d"),this.clear(),this._handleMouseDown=function(a){1===a.which&&(c._mouseButtonDown=!0,c._strokeBegin(a))},this._handleMouseMove=function(a){c._mouseButtonDown&&c._strokeUpdate(a)},this._handleMouseUp=function(a){1===a.which&&c._mouseButtonDown&&(c._mouseButtonDown=!1,c._strokeEnd(a))},this._handleTouchStart=function(a){if(1==a.targetTouches.length){var b=a.changedTouches[0];c._strokeBegin(b)}},this._handleTouchMove=function(a){a.preventDefault();var b=a.targetTouches[0];c._strokeUpdate(b)},this._handleTouchEnd=function(a){var b=a.target===c._canvas;b&&(a.preventDefault(),c._strokeEnd(a))},this._handleMouseEvents(),this._handleTouchEvents()};b.prototype.clear=function(){var a=this._ctx,b=this._canvas;a.fillStyle=this.backgroundColor,a.clearRect(0,0,b.width,b.height),a.fillRect(0,0,b.width,b.height),this._reset()},b.prototype.toDataURL=function(){var a=this._canvas;return a.toDataURL.apply(a,arguments)},b.prototype.fromDataURL=function(a){var b=this,c=new Image,d=window.devicePixelRatio||1,e=this._canvas.width/d,f=this._canvas.height/d;this._reset(),c.src=a,c.onload=function(){b._ctx.drawImage(c,0,0,e,f)},this._isEmpty=!1},b.prototype._strokeUpdate=function(a){var b=this._createPoint(a);this._addPoint(b)},b.prototype._strokeBegin=function(a){this._reset(),this._strokeUpdate(a),"function"==typeof this.onBegin&&this.onBegin(a)},b.prototype._strokeDraw=function(a){var b=this._ctx,c="function"==typeof this.dotSize?this.dotSize():this.dotSize;b.beginPath(),this._drawPoint(a.x,a.y,c),b.closePath(),b.fill()},b.prototype._strokeEnd=function(a){var b=this.points.length>2,c=this.points[0];!b&&c&&this._strokeDraw(c),"function"==typeof this.onEnd&&this.onEnd(a)},b.prototype._handleMouseEvents=function(){this._mouseButtonDown=!1,this._canvas.addEventListener("mousedown",this._handleMouseDown),this._canvas.addEventListener("mousemove",this._handleMouseMove),a.addEventListener("mouseup",this._handleMouseUp)},b.prototype._handleTouchEvents=function(){this._canvas.style.msTouchAction="none",this._canvas.style.touchAction="none",this._canvas.addEventListener("touchstart",this._handleTouchStart),this._canvas.addEventListener("touchmove",this._handleTouchMove),this._canvas.addEventListener("touchend",this._handleTouchEnd)},b.prototype.on=function(){this._handleMouseEvents(),this._handleTouchEvents()},b.prototype.off=function(){this._canvas.removeEventListener("mousedown",this._handleMouseDown),this._canvas.removeEventListener("mousemove",this._handleMouseMove),a.removeEventListener("mouseup",this._handleMouseUp),this._canvas.removeEventListener("touchstart",this._handleTouchStart),this._canvas.removeEventListener("touchmove",this._handleTouchMove),this._canvas.removeEventListener("touchend",this._handleTouchEnd)},b.prototype.isEmpty=function(){return this._isEmpty},b.prototype._reset=function(){this.points=[],this._lastVelocity=0,this._lastWidth=(this.minWidth+this.maxWidth)/2,this._isEmpty=!0,this._ctx.fillStyle=this.penColor},b.prototype._createPoint=function(a){var b=this._canvas.getBoundingClientRect();return new c(a.clientX-b.left,a.clientY-b.top)},b.prototype._addPoint=function(a){var b,c,e,f,g=this.points;g.push(a),g.length>2&&(3===g.length&&g.unshift(g[0]),f=this._calculateCurveControlPoints(g[0],g[1],g[2]),b=f.c2,f=this._calculateCurveControlPoints(g[1],g[2],g[3]),c=f.c1,e=new d(g[1],b,c,g[2]),this._addCurve(e),g.shift())},b.prototype._calculateCurveControlPoints=function(a,b,d){var e=a.x-b.x,f=a.y-b.y,g=b.x-d.x,h=b.y-d.y,i={x:(a.x+b.x)/2,y:(a.y+b.y)/2},j={x:(b.x+d.x)/2,y:(b.y+d.y)/2},k=Math.sqrt(e*e+f*f),l=Math.sqrt(g*g+h*h),m=i.x-j.x,n=i.y-j.y,o=l/(k+l),p={x:j.x+m*o,y:j.y+n*o},q=b.x-p.x,r=b.y-p.y;return{c1:new c(i.x+q,i.y+r),c2:new c(j.x+q,j.y+r)}},b.prototype._addCurve=function(a){var b,c,d=a.startPoint,e=a.endPoint;b=e.velocityFrom(d),b=this.velocityFilterWeight*b+(1-this.velocityFilterWeight)*this._lastVelocity,c=this._strokeWidth(b),this._drawCurve(a,this._lastWidth,c),this._lastVelocity=b,this._lastWidth=c},b.prototype._drawPoint=function(a,b,c){var d=this._ctx;d.moveTo(a,b),d.arc(a,b,c,0,2*Math.PI,!1),this._isEmpty=!1},b.prototype._drawCurve=function(a,b,c){var d,e,f,g,h,i,j,k,l,m,n,o=this._ctx,p=c-b;for(d=Math.floor(a.length()),o.beginPath(),f=0;d>f;f++)g=f/d,h=g*g,i=h*g,j=1-g,k=j*j,l=k*j,m=l*a.startPoint.x,m+=3*k*g*a.control1.x,m+=3*j*h*a.control2.x,m+=i*a.endPoint.x,n=l*a.startPoint.y,n+=3*k*g*a.control1.y,n+=3*j*h*a.control2.y,n+=i*a.endPoint.y,e=b+i*p,this._drawPoint(m,n,e);o.closePath(),o.fill()},b.prototype._strokeWidth=function(a){return Math.max(this.maxWidth/(a+1),this.minWidth)};var c=function(a,b,c){this.x=a,this.y=b,this.time=c||(new Date).getTime()};c.prototype.velocityFrom=function(a){return this.time!==a.time?this.distanceTo(a)/(this.time-a.time):1},c.prototype.distanceTo=function(a){return Math.sqrt(Math.pow(this.x-a.x,2)+Math.pow(this.y-a.y,2))};var d=function(a,b,c,d){this.startPoint=a,this.control1=b,this.control2=c,this.endPoint=d};return d.prototype.length=function(){var a,b,c,d,e,f,g,h,i=10,j=0;for(a=0;i>=a;a++)b=a/i,c=this._point(b,this.startPoint.x,this.control1.x,this.control2.x,this.endPoint.x),d=this._point(b,this.startPoint.y,this.control1.y,this.control2.y,this.endPoint.y),a>0&&(g=c-e,h=d-f,j+=Math.sqrt(g*g+h*h)),e=c,f=d;return j},d.prototype._point=function(a,b,c,d,e){return b*(1-a)*(1-a)*(1-a)+3*c*(1-a)*(1-a)*a+3*d*(1-a)*a*a+e*a*a*a},b}(document);return a}); \ No newline at end of file diff --git a/resources/lang/en/admin/settings/general.php b/resources/lang/en/admin/settings/general.php index 1d7117af1..1a6b2d479 100644 --- a/resources/lang/en/admin/settings/general.php +++ b/resources/lang/en/admin/settings/general.php @@ -121,6 +121,8 @@ return array( 'two_factor_config_complete' => 'Submit Code', 'two_factor_enabled_edit_not_allowed' => 'Your administrator does not permit you to edit this setting.', 'two_factor_enrollment_text' => "Two factor authentication is required, however your device has not been enrolled yet. Open your Google Authenticator app and scan the QR code below to enroll your device. Once you've enrolled your device, enter the code below", + 'require_accept_signature' => 'Require Signature', + 'require_accept_signature_help_text' => 'Enabling this feature will require users to physically sign off on accepting an asset.', 'left' => 'left', 'right' => 'right', 'top' => 'top', diff --git a/resources/lang/en/general.php b/resources/lang/en/general.php index cc6e02560..5aaa8359e 100644 --- a/resources/lang/en/general.php +++ b/resources/lang/en/general.php @@ -145,6 +145,7 @@ 'select_asset' => 'Select Asset', 'settings' => 'Settings', 'sign_in' => 'Sign in', + 'signature' => 'Signature', 'some_features_disabled' => 'DEMO MODE: Some features are disabled for this installation.', 'site_name' => 'Site Name', 'state' => 'State', diff --git a/resources/views/account/accept-asset.blade.php b/resources/views/account/accept-asset.blade.php index 4d81992ef..787544546 100644 --- a/resources/views/account/accept-asset.blade.php +++ b/resources/views/account/accept-asset.blade.php @@ -2,59 +2,142 @@ {{-- Page title --}} @section('title') - -@parent + Accept {{ $item->showAssetName() }} + @parent @stop + {{-- Page content --}} @section('content') + + + -
{{ trans('admin/settings/general.require_accept_signature_help_text') }}
+