diff --git a/lam/HISTORY b/lam/HISTORY
index 8e72b205..bcae7f22 100644
--- a/lam/HISTORY
+++ b/lam/HISTORY
@@ -3,6 +3,7 @@ September 2020
- Configuration export and import
- Show password prompt when a user with expired password logs into LAM admin interface (requires PHP 7.2)
- Better error messages on login when account is expired/deactivated/...
+ - Personal: photo can be uploaded via webcam
- Windows users: group display format can be configured (cn/dn)
- LAM Pro:
-> Windows: new cron job to send users a summary of their managed groups
diff --git a/lam/graphics/webcam.png b/lam/graphics/webcam.png
new file mode 100644
index 00000000..f55a6f3d
Binary files /dev/null and b/lam/graphics/webcam.png differ
diff --git a/lam/lib/html.inc b/lam/lib/html.inc
index 21fb7b7d..58a4cd78 100644
--- a/lam/lib/html.inc
+++ b/lam/lib/html.inc
@@ -3078,7 +3078,13 @@ class htmlStatusMessage extends htmlElement {
* @return array List of input field names and their type (name => type)
*/
public function generateHTML($module, $input, $values, $restricted, &$tabindex, $scope) {
+ if (!empty($this->cssClasses)) {
+ echo '
';
+ }
StatusMessage($this->type, $this->title, $this->text, $this->params);
+ if (!empty($this->cssClasses)) {
+ echo '
';
+ }
return array();
}
@@ -3344,6 +3350,8 @@ class htmlLink extends htmlElement {
private $onClick = null;
/** show as button */
private $showAsButton = false;
+ /** link id */
+ private $id;
/**
* Constructor.
@@ -3393,8 +3401,8 @@ class htmlLink extends htmlElement {
$onClick = ' onclick="' . $this->onClick . '"';
}
$idAttr = '';
- if ($this->showAsButton) {
- $id = 'a_' . preg_replace('/[^a-zA-Z0-9_]+/', '_', $this->target);
+ if ($this->showAsButton || !empty($this->id)) {
+ $id = !empty($this->id) ? $this->id : 'a_' . preg_replace('/[^a-zA-Z0-9_]+/', '_', $this->target);
$idAttr = ' id="' . $id . '"';
}
$classAttr = '';
@@ -3457,6 +3465,15 @@ class htmlLink extends htmlElement {
$this->onClick = htmlspecialchars($event);
}
+ /**
+ * Sets the element id.
+ *
+ * @param string $id unique id
+ */
+ public function setId($id) {
+ $this->id = $id;
+ }
+
}
/**
@@ -4973,5 +4990,68 @@ class htmlResponsiveTable extends htmlElement {
}
+/**
+ * Renders a canvas.
+ *
+ * @author Roland Gruber
+ */
+class htmlCanvas extends htmlElement {
+
+ private $id;
+
+ /**
+ * Constructor
+ *
+ * @param string $id html id
+ */
+ public function __construct($id) {
+ $this->id = $id;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ function generateHTML($module, $input, $values, $restricted, &$tabindex, $scope) {
+ $classesValue = '';
+ if (!empty($this->cssClasses)) {
+ $classesValue = ' class="' . implode(' ', $this->cssClasses) . '"';
+ }
+ echo '';
+ echo ' ';
+ return array();
+ }
+}
+
+/**
+ * Renders a video.
+ *
+ * @author Roland Gruber
+ */
+class htmlVideo extends htmlElement {
+
+ private $id;
+
+ /**
+ * Constructor
+ *
+ * @param string $id html id
+ */
+ public function __construct($id) {
+ $this->id = $id;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ function generateHTML($module, $input, $values, $restricted, &$tabindex, $scope) {
+ $classesValue = '';
+ if (!empty($this->cssClasses)) {
+ $classesValue = ' class="' . implode(' ', $this->cssClasses) . '"';
+ }
+ echo '';
+ echo ' ';
+ return array();
+ }
+}
?>
diff --git a/lam/lib/modules/inetOrgPerson.inc b/lam/lib/modules/inetOrgPerson.inc
index 47fbeef2..b1960f7b 100644
--- a/lam/lib/modules/inetOrgPerson.inc
+++ b/lam/lib/modules/inetOrgPerson.inc
@@ -1629,7 +1629,7 @@ class inetOrgPerson extends baseModule implements passwordService {
if ($this->isAdminReadOnly('jpegPhoto')) {
return array();
}
- if (isset($_POST['form_subpage_' . get_class($this) . '_photo_upload'])) {
+ if (isset($_POST['form_subpage_' . get_class($this) . '_photo_upload']) || isset($_POST['webcamData'])) {
return $this->uploadPhoto();
}
if (isset($_POST['form_subpage_' . get_class($this) . '_attributes_crop'])) {
@@ -1656,40 +1656,46 @@ class inetOrgPerson extends baseModule implements passwordService {
*/
private function uploadPhoto() {
$messages = array();
- if ($_FILES['photoFile'] && ($_FILES['photoFile']['size'] > 0)) {
+ if ((empty($_FILES['photoFile']) || ($_FILES['photoFile']['size'] <= 0)) && empty($_POST['webcamData'])) {
+ $messages[] = $this->messages['file'][0];
+ return $messages;
+ }
+ if (!empty($_FILES['photoFile']['tmp_name'])) {
$handle = fopen($_FILES['photoFile']['tmp_name'], "r");
$data = fread($handle, 100000000);
+ fclose($handle);
if (!empty($this->moduleSettings['inetOrgPerson_jpegPhoto_maxSize'][0]) && (strlen($data) > (1024 * $this->moduleSettings['inetOrgPerson_jpegPhoto_maxSize'][0]))) {
$errMsg = $this->messages['file'][3];
$errMsg[] = null;
$errMsg[] = array($this->moduleSettings['inetOrgPerson_jpegPhoto_maxSize'][0]);
return array($errMsg);
}
- fclose($handle);
- // convert to JPG
- try {
- include_once dirname(__FILE__) . '/../imageutils.inc';
- $imageManipulator = ImageManipulationFactory::getImageManipulator($data);
- // resize if maximum values specified
- if (!empty($this->moduleSettings['inetOrgPerson_jpegPhoto_maxWidth'][0]) || !empty($this->moduleSettings['inetOrgPerson_jpegPhoto_maxHeight'][0])) {
- $maxWidth = empty($this->moduleSettings['inetOrgPerson_jpegPhoto_maxWidth'][0]) ? $imageManipulator->getWidth() : $this->moduleSettings['inetOrgPerson_jpegPhoto_maxWidth'][0];
- $maxHeight = empty($this->moduleSettings['inetOrgPerson_jpegPhoto_maxHeight'][0]) ? $imageManipulator->getHeight() : $this->moduleSettings['inetOrgPerson_jpegPhoto_maxHeight'][0];
- $imageManipulator->thumbnail($maxWidth, $maxHeight);
- }
- $imageManipulator->convertToJpeg();
- $data = $imageManipulator->getImageData();
- }
- catch (Exception $e) {
- $msg = $this->messages['file'][2];
- $msg[] = htmlspecialchars($e->getMessage());
- $messages[] = $msg;
- return $messages;
- }
- $this->attributes['jpegPhoto'][0] = $data;
}
- else {
- $messages[] = $this->messages['file'][0];
+ elseif (isset($_POST['webcamData'])) {
+ $data = $_POST['webcamData'];
+ $data = str_replace('data:image/png;base64,', '', $data);
+ $data = base64_decode($data);
+ }
+ // convert to JPG
+ try {
+ include_once dirname(__FILE__) . '/../imageutils.inc';
+ $imageManipulator = ImageManipulationFactory::getImageManipulator($data);
+ // resize if maximum values specified
+ if (!empty($this->moduleSettings['inetOrgPerson_jpegPhoto_maxWidth'][0]) || !empty($this->moduleSettings['inetOrgPerson_jpegPhoto_maxHeight'][0])) {
+ $maxWidth = empty($this->moduleSettings['inetOrgPerson_jpegPhoto_maxWidth'][0]) ? $imageManipulator->getWidth() : $this->moduleSettings['inetOrgPerson_jpegPhoto_maxWidth'][0];
+ $maxHeight = empty($this->moduleSettings['inetOrgPerson_jpegPhoto_maxHeight'][0]) ? $imageManipulator->getHeight() : $this->moduleSettings['inetOrgPerson_jpegPhoto_maxHeight'][0];
+ $imageManipulator->thumbnail($maxWidth, $maxHeight);
+ }
+ $imageManipulator->convertToJpeg();
+ $data = $imageManipulator->getImageData();
+ }
+ catch (Exception $e) {
+ $msg = $this->messages['file'][2];
+ $msg[] = htmlspecialchars($e->getMessage());
+ $messages[] = $msg;
+ return $messages;
}
+ $this->attributes['jpegPhoto'][0] = $data;
return $messages;
}
@@ -1704,9 +1710,33 @@ class inetOrgPerson extends baseModule implements passwordService {
$container->add(new htmlSubTitle(_('Upload image')), 12);
$label = _('Photo file');
$container->add(new htmlResponsiveInputFileUpload('photoFile', $label, 'photoUpload'), 12);
+ $container->addVerticalSpacer('0.5rem');
+ $container->addLabel(new htmlOutputText(' ', false));
+ $container->addField(new htmlAccountPageButton(get_class($this), 'photo', 'upload', _('Upload')));
+ $container->addVerticalSpacer('1rem');
+ $webcamContent = new htmlResponsiveRow();
+ $webcamContent->add(new htmlSubTitle(_('Use webcam')), 12);
+ $errorMessage = new htmlStatusMessage('ERROR', '');
+ $errorMessage->setCSSClasses(array('hidden', 'lam-webcam-message'));
+ $webcamContent->add($errorMessage, 12);
+ $captureButton = new htmlButton('lam-webcam-capture', _('Start capture'));
+ $captureButton->setOnClick('window.lam.tools.webcam.capture(event);');
+ $webcamContent->add($captureButton, 12, 12, 12, 'text-center');
+ $video = new htmlVideo('lam-webcam-video');
+ $video->setCSSClasses(array('hidden'));
+ $webcamContent->add($video, 12, 12, 12, 'text-center');
+ $webcamContent->addVerticalSpacer('0.5rem');
+ $webcamUploadButton = new htmlButton('uploadWebcam', _('Upload'));
+ $webcamUploadButton->setCSSClasses(array('btn-lam-webcam-upload', 'hidden'));
+ $webcamUploadButton->setOnClick('window.lam.tools.webcam.upload();');
+ $webcamContent->add($webcamUploadButton, 12, 12, 12, 'text-center');
+ $canvas = new htmlCanvas('lam-webcam-canvas');
+ $canvas->setCSSClasses(array('hidden'));
+ $webcamContent->add($canvas, 12);
+ $webcamDiv = new htmlDiv('lam_webcam_div', $webcamContent, array('hidden'));
+ $container->add($webcamDiv, 12);
$container->addVerticalSpacer('1rem');
- $container->addLabel(new htmlAccountPageButton(get_class($this), 'photo', 'upload', _('Upload')));
- $container->addField(new htmlAccountPageButton(get_class($this), 'attributes', 'back', _('Back')));
+ $container->add(new htmlAccountPageButton(get_class($this), 'attributes', 'back', _('Back')), 12);
}
else {
$container->add(new htmlSubTitle(_('Crop image')), 12);
@@ -3062,6 +3092,33 @@ class inetOrgPerson extends baseModule implements passwordService {
$uploadStatus = new htmlDiv('inetOrgPersonPhotoUploadStatus', new htmlOutputText(''));
$uploadStatus->setCSSClasses(array('qq-upload-list'));
$row->add($uploadStatus, 12);
+ // webcam button
+ $webcamContent = new htmlResponsiveRow();
+ $webcamContent->addVerticalSpacer('0.5rem');
+ $errorMessage = new htmlStatusMessage('ERROR', '');
+ $errorMessage->setCSSClasses(array('hidden', 'lam-webcam-message'));
+ $webcamContent->add($errorMessage, 12);
+ $webcamContent->addVerticalSpacer('0.5rem');
+ $captureButton = new htmlLink(_('Use webcam'), '#', '../../graphics/webcam.png', true);
+ $captureButton->setId('btn_lam-webcam-capture');
+ $captureButton->setOnClick('window.lam.tools.webcam.capture(event);');
+ $webcamContent->add($captureButton, 12, 12, 12);
+ $video = new htmlVideo('lam-webcam-video');
+ $video->setCSSClasses(array('hidden'));
+ $webcamContent->add($video, 12, 12, 12, 'text-center');
+ $webcamContent->addVerticalSpacer('1rem');
+ $webcamUploadButton = new htmlLink(_('Upload'), '#', '../../graphics/up.gif', true);
+ $webcamUploadButton->setId('btn-lam-webcam-upload');
+ $webcamUploadButton->setCSSClasses(array('btn-lam-webcam-upload', 'hidden'));
+ $webcamUploadButton->setOnClick('window.lam.tools.webcam.uploadSelfService(event, "' . getSecurityTokenName()
+ . '", "' . getSecurityTokenValue() . '", "inetOrgPerson", "user", "' . _('File upload failed!') . '", "inetOrgPersonPhotoUploadContent");');
+ $webcamContent->add($webcamUploadButton, 12, 12, 12);
+ $canvas = new htmlCanvas('lam-webcam-canvas');
+ $canvas->setCSSClasses(array('hidden'));
+ $webcamContent->add($canvas, 12);
+ $webcamDiv = new htmlDiv('lam_webcam_div', $webcamContent, array('hidden'));
+ $webcamContent->addVerticalSpacer('1rem');
+ $row->add($webcamDiv, 12);
return $row;
}
@@ -3095,6 +3152,7 @@ class inetOrgPerson extends baseModule implements passwordService {
if (data.success) {
if (data.html) {
jQuery(\'#inetOrgPersonPhotoUploadContent\').html(data.html);
+ window.lam.tools.webcam.init();
}
}
else {
@@ -3119,6 +3177,7 @@ class inetOrgPerson extends baseModule implements passwordService {
function inetOrgPersonDeletePhotoHandleReply(data) {
if (data.errorsOccured == "false") {
jQuery(\'#inetOrgPersonPhotoUploadContent\').html(data.html);
+ window.lam.tools.webcam.init();
}
else {
alert(data.errormessage);
@@ -3790,13 +3849,20 @@ class inetOrgPerson extends baseModule implements passwordService {
*/
private function ajaxUploadPhoto() {
$result = array('success' => true);
- if (!isset($_FILES['qqfile']) || ($_FILES['qqfile']['size'] < 100)) {
+ if ((!isset($_FILES['qqfile']) || ($_FILES['qqfile']['size'] < 100)) && empty($_POST['webcamData'])) {
$result = array('error' => _('No file received.'));
}
else {
- $handle = fopen($_FILES['qqfile']['tmp_name'], "r");
- $data = fread($handle, 100000000);
- fclose($handle);
+ if (empty($_POST['webcamData'])) {
+ $handle = fopen($_FILES['qqfile']['tmp_name'], "r");
+ $data = fread($handle, 100000000);
+ fclose($handle);
+ }
+ else {
+ $data = $_POST['webcamData'];
+ $data = str_replace('data:image/png;base64,', '', $data);
+ $data = base64_decode($data);
+ }
try {
include_once dirname(__FILE__) . '/../imageutils.inc';
$imageManipulator = ImageManipulationFactory::getImageManipulator($data);
diff --git a/lam/lib/modules/windowsUser.inc b/lam/lib/modules/windowsUser.inc
index ade505f0..20cd6fb2 100644
--- a/lam/lib/modules/windowsUser.inc
+++ b/lam/lib/modules/windowsUser.inc
@@ -2087,9 +2087,33 @@ class windowsUser extends baseModule implements passwordService {
$container->add(new htmlSubTitle(_('Upload image')), 12);
$label = _('Photo file');
$container->add(new htmlResponsiveInputFileUpload('photoFile', $label, 'photoUpload'), 12);
- $container->addVerticalSpacer('2rem');
- $container->addLabel(new htmlAccountPageButton(get_class($this), 'photo', 'upload', _('Upload')));
- $container->addField(new htmlAccountPageButton(get_class($this), 'attributes', 'back', _('Back')));
+ $container->addVerticalSpacer('0.5rem');
+ $container->addLabel(new htmlOutputText(' ', false));
+ $container->addField(new htmlAccountPageButton(get_class($this), 'photo', 'upload', _('Upload')));
+ $container->addVerticalSpacer('1rem');
+ $webcamContent = new htmlResponsiveRow();
+ $webcamContent->add(new htmlSubTitle(_('Use webcam')), 12);
+ $errorMessage = new htmlStatusMessage('ERROR', '');
+ $errorMessage->setCSSClasses(array('hidden', 'lam-webcam-message'));
+ $webcamContent->add($errorMessage, 12);
+ $captureButton = new htmlButton('lam-webcam-capture', _('Start capture'));
+ $captureButton->setOnClick('window.lam.tools.webcam.capture(event);');
+ $webcamContent->add($captureButton, 12, 12, 12, 'text-center');
+ $video = new htmlVideo('lam-webcam-video');
+ $video->setCSSClasses(array('hidden'));
+ $webcamContent->add($video, 12, 12, 12, 'text-center');
+ $webcamContent->addVerticalSpacer('0.5rem');
+ $webcamUploadButton = new htmlButton('uploadWebcam', _('Upload'));
+ $webcamUploadButton->setCSSClasses(array('btn-lam-webcam-upload', 'hidden'));
+ $webcamUploadButton->setOnClick('window.lam.tools.webcam.upload();');
+ $webcamContent->add($webcamUploadButton, 12, 12, 12, 'text-center');
+ $canvas = new htmlCanvas('lam-webcam-canvas');
+ $canvas->setCSSClasses(array('hidden'));
+ $webcamContent->add($canvas, 12);
+ $webcamDiv = new htmlDiv('lam_webcam_div', $webcamContent, array('hidden'));
+ $container->add($webcamDiv, 12);
+ $container->addVerticalSpacer('1rem');
+ $container->add(new htmlAccountPageButton(get_class($this), 'attributes', 'back', _('Back')), 12);
}
else {
$container->add(new htmlSubTitle(_('Crop image')), 12);
@@ -2115,7 +2139,7 @@ class windowsUser extends baseModule implements passwordService {
if (isset($_POST['form_subpage_' . get_class($this) . '_attributes_back'])) {
return array();
}
- if (isset($_POST['form_subpage_' . get_class($this) . '_photo_upload'])) {
+ if (isset($_POST['form_subpage_' . get_class($this) . '_photo_upload']) || isset($_POST['webcamData'])) {
return $this->uploadPhoto();
}
if (isset($_POST['form_subpage_' . get_class($this) . '_attributes_crop'])) {
@@ -2142,40 +2166,46 @@ class windowsUser extends baseModule implements passwordService {
*/
private function uploadPhoto() {
$messages = array();
- if ($_FILES['photoFile'] && ($_FILES['photoFile']['size'] > 0)) {
+ if ((empty($_FILES['photoFile']) || ($_FILES['photoFile']['size'] <= 0)) && empty($_POST['webcamData'])) {
+ $messages[] = $this->messages['file'][0];
+ return $messages;
+ }
+ if (!empty($_FILES['photoFile']['tmp_name'])) {
$handle = fopen($_FILES['photoFile']['tmp_name'], "r");
$data = fread($handle, 10000000);
+ fclose($handle);
if (!empty($this->moduleSettings['windowsUser_jpegPhoto_maxSize'][0]) && (strlen($data) > (1024 * $this->moduleSettings['windowsUser_jpegPhoto_maxSize'][0]))) {
$errMsg = $this->messages['file'][3];
$errMsg[] = null;
$errMsg[] = array($this->moduleSettings['windowsUser_jpegPhoto_maxSize'][0]);
return array($errMsg);
}
- fclose($handle);
- // convert to JPG
- try {
- include_once dirname(__FILE__) . '/../imageutils.inc';
- $imageManipulator = ImageManipulationFactory::getImageManipulator($data);
- // resize if maximum values specified
- if (!empty($this->moduleSettings['windowsUser_jpegPhoto_maxWidth'][0]) || !empty($this->moduleSettings['windowsUser_jpegPhoto_maxHeight'][0])) {
- $maxWidth = empty($this->moduleSettings['windowsUser_jpegPhoto_maxWidth'][0]) ? $imageManipulator->getWidth() : $this->moduleSettings['windowsUser_jpegPhoto_maxWidth'][0];
- $maxHeight = empty($this->moduleSettings['windowsUser_jpegPhoto_maxHeight'][0]) ? $imageManipulator->getHeight() : $this->moduleSettings['windowsUser_jpegPhoto_maxHeight'][0];
- $imageManipulator->thumbnail($maxWidth, $maxHeight);
- }
- $imageManipulator->convertToJpeg();
- $data = $imageManipulator->getImageData();
- }
- catch (Exception $e) {
- $msg = $this->messages['file'][2];
- $msg[] = htmlspecialchars($e->getMessage());
- $messages[] = $msg;
- return $messages;
- }
- $this->attributes['jpegPhoto'][0] = $data;
}
- else {
- $messages[] = $this->messages['file'][0];
+ elseif (isset($_POST['webcamData'])) {
+ $data = $_POST['webcamData'];
+ $data = str_replace('data:image/png;base64,', '', $data);
+ $data = base64_decode($data);
+ }
+ // convert to JPG
+ try {
+ include_once dirname(__FILE__) . '/../imageutils.inc';
+ $imageManipulator = ImageManipulationFactory::getImageManipulator($data);
+ // resize if maximum values specified
+ if (!empty($this->moduleSettings['windowsUser_jpegPhoto_maxWidth'][0]) || !empty($this->moduleSettings['windowsUser_jpegPhoto_maxHeight'][0])) {
+ $maxWidth = empty($this->moduleSettings['windowsUser_jpegPhoto_maxWidth'][0]) ? $imageManipulator->getWidth() : $this->moduleSettings['windowsUser_jpegPhoto_maxWidth'][0];
+ $maxHeight = empty($this->moduleSettings['windowsUser_jpegPhoto_maxHeight'][0]) ? $imageManipulator->getHeight() : $this->moduleSettings['windowsUser_jpegPhoto_maxHeight'][0];
+ $imageManipulator->thumbnail($maxWidth, $maxHeight);
+ }
+ $imageManipulator->convertToJpeg();
+ $data = $imageManipulator->getImageData();
+ }
+ catch (Exception $e) {
+ $msg = $this->messages['file'][2];
+ $msg[] = htmlspecialchars($e->getMessage());
+ $messages[] = $msg;
+ return $messages;
}
+ $this->attributes['jpegPhoto'][0] = $data;
return $messages;
}
@@ -4349,7 +4379,7 @@ if (interface_exists('\LAM\JOB\Job', false)) {
/**
* Returns the month interval.
*
- * @param arry $options config options
+ * @param array $options config options
* @param $jobId job id
* @return int interval
*/
diff --git a/lam/style/responsive/120_lam.css b/lam/style/responsive/120_lam.css
index 190b9c42..174983a5 100644
--- a/lam/style/responsive/120_lam.css
+++ b/lam/style/responsive/120_lam.css
@@ -193,6 +193,11 @@ table.responsive-table td {
padding: 5px 5px 5px 5px;
}
+ #lam-webcam-video {
+ max-width: 200px;
+ max-height: 200px;
+ }
+
}
/* tablet */
@@ -222,6 +227,11 @@ table.responsive-table td {
padding: 5px 20px 5px 20px;
}
+ #lam-webcam-video {
+ max-width: 300px;
+ max-height: 300px;
+ }
+
}
/* desktop */
@@ -255,4 +265,9 @@ table.responsive-table td {
padding: 5px 20px 5px 20px;
}
+ #lam-webcam-video {
+ max-width: 400px;
+ max-height: 400px;
+ }
+
}
diff --git a/lam/templates/lib/500_lam.js b/lam/templates/lib/500_lam.js
index d7839e40..5a451667 100644
--- a/lam/templates/lib/500_lam.js
+++ b/lam/templates/lib/500_lam.js
@@ -934,6 +934,144 @@ window.lam.tools.setInitialFocus = function() {
jQuery('.lam-initial-focus').focus();
};
+window.lam.tools.webcam = window.lam.tools.webcam || {};
+
+/**
+ * Initializes the webcam capture.
+ */
+window.lam.tools.webcam.init = function() {
+ var contentDiv = jQuery('#lam_webcam_div');
+ if (contentDiv.length === 0) {
+ return;
+ }
+ if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
+ navigator.mediaDevices.enumerateDevices()
+ .then(function(mediaDevices) {
+ mediaDevices.forEach(mediaDevice => {
+ if (mediaDevice.kind === 'videoinput') {
+ contentDiv.show();
+ }
+ });
+ });
+ }
+};
+
+/**
+ * Starts the webcam capture.
+ */
+window.lam.tools.webcam.capture = function(event) {
+ event.preventDefault();
+ var video = document.getElementById('lam-webcam-video');
+ var msg = jQuery('.lam-webcam-message');
+ msg.hide();
+ navigator.mediaDevices.getUserMedia({
+ video: {
+ facingMode: 'user',
+ width: { min: 1024, ideal: 1280, max: 1920 },
+ height: { min: 576, ideal: 720, max: 1080 }
+ },
+ audio: false
+ })
+ .then(function(stream) {
+ video.srcObject = stream;
+ video.play();
+ window.lam.tools.webcamStream = stream;
+ jQuery('#btn_lam-webcam-capture').hide();
+ jQuery('.btn-lam-webcam-upload').show();
+ jQuery('#lam-webcam-video').show();
+ })
+ .catch(function(err) {
+ msg.find('.statusTitle').text(err);
+ msg.show();
+ });
+ return false;
+}
+
+/**
+ * Starts the webcam upload.
+ */
+window.lam.tools.webcam.upload = function() {
+ var form = jQuery('#lam-webcam-canvas').closest('form');
+ canvasData = window.lam.tools.webcam.prepareData();
+ var canvasDataInput = jQuery(" ");
+ canvasDataInput.attr('name', 'webcamData');
+ canvasDataInput.attr('id', 'webcamData');
+ canvasDataInput.attr('type', 'hidden');
+ canvasDataInput.attr('value', canvasData);
+ form.append(canvasDataInput);
+ form.submit();
+ return true;
+}
+
+/**
+ * Starts the webcam upload.
+ *
+ * @param event click event
+ * @param tokenName security token name
+ * @param tokenValue security token value
+ * @param moduleName module name
+ * @param scope account type
+ * @param uploadErrorMessage error message if upload fails
+ * @param contentId id of content to replace
+ */
+window.lam.tools.webcam.uploadSelfService = function(event, tokenName, tokenValue, moduleName, scope, uploadErrorMessage, contentId) {
+ event.preventDefault();
+ var msg = jQuery('.lam-webcam-message');
+ canvasData = window.lam.tools.webcam.prepareData();
+ var data = {
+ webcamData: canvasData
+ };
+ data[tokenName] = tokenValue;
+ jQuery.ajax({
+ url: '../misc/ajax.php?selfservice=1&action=ajaxPhotoUpload'
+ + '&module=' + moduleName + '&scope=' + scope,
+ method: 'POST',
+ data: data
+ })
+ .done(function(jsonData) {
+ if (jsonData.success) {
+ if (jsonData.html) {
+ jQuery('#' + contentId).html(jsonData.html);
+ window.lam.tools.webcam.init();
+ }
+ return false;
+ }
+ else if (jsonData.error) {
+ msg.find('.statusTitle').text(jsonData.error);
+ msg.show();
+ }
+ })
+ .fail(function() {
+ msg.find('.statusTitle').text(errorMessage);
+ msg.show();
+ });
+ jQuery('#btn_lam-webcam-capture').show();
+ jQuery('.btn-lam-webcam-upload').hide();
+ return false;
+}
+
+/**
+ * Starts the webcam upload.
+ *
+ * @return webcam data as string
+ */
+window.lam.tools.webcam.prepareData = function() {
+ var canvas = document.getElementById('lam-webcam-canvas');
+ var video = document.getElementById('lam-webcam-video');
+ canvas.setAttribute('width', video.videoWidth);
+ canvas.setAttribute('height', video.videoHeight);
+ var context = canvas.getContext('2d');
+ context.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
+ var canvasData = canvas.toDataURL("image/png");
+ video.pause();
+ window.lam.tools.webcamStream.getTracks().forEach(function(track) {
+ track.stop();
+ });
+ jQuery(canvas).hide();
+ jQuery(video).hide();
+ return canvasData;
+}
+
window.lam.tools.schema = window.lam.tools.schema || {};
/**
@@ -1788,6 +1926,7 @@ jQuery(document).ready(function() {
window.lam.tools.addSavedSelectListener();
window.lam.tools.activateTab();
window.lam.tools.setInitialFocus();
+ window.lam.tools.webcam.init();
window.lam.tools.schema.select();
window.lam.html.activateLightboxes();
window.lam.html.preventEnter();
diff --git a/lam/tests/lib/modules/windowsUserTest.php b/lam/tests/lib/modules/windowsUserTest.php
index 4d109ae3..1e137bd2 100644
--- a/lam/tests/lib/modules/windowsUserTest.php
+++ b/lam/tests/lib/modules/windowsUserTest.php
@@ -80,6 +80,7 @@ use PHPUnit\Framework\TestCase;
public function testWindowsManagedGroupsNotifyJob_getLastEffectiveExecutionDate() {
if (!interface_exists('\LAM\JOB\Job', false)) {
+ $this->markTestSkipped();
return;
}
$resultLog = new \LAM\JOB\JobResultLog();
diff --git a/lam/tests/lib/persistenceTest.php b/lam/tests/lib/persistenceTest.php
index dbad924f..6a107dee 100644
--- a/lam/tests/lib/persistenceTest.php
+++ b/lam/tests/lib/persistenceTest.php
@@ -86,7 +86,7 @@ class ConfigDataExporterTest extends TestCase {
$exporter = $this->getMockBuilder('\LAM\PERSISTENCE\ConfigDataExporter')
->setMethods(array('_getMainConfigData', '_getCertificates', '_getServerProfiles',
'_getAccountProfiles', '_getAccountProfileTemplates', '_getPdfProfiles',
- '_getPdfProfileTemplates', '_getSelfServiceProfiles'))
+ '_getPdfProfileTemplates', '_getSelfServiceProfiles', '_getWebauthn'))
->getMock();
$exporter->method('_getMainConfigData')->willReturn($mainData);
$exporter->method('_getCertificates')->willReturn('certs');