added cropping support

This commit is contained in:
Roland Gruber 2018-05-12 10:43:32 +02:00
parent 5ec89293b2
commit 137ef0721f
5 changed files with 4095 additions and 24 deletions

View File

@ -208,5 +208,6 @@ templates/lib/*jquery*.js B 2010 John Resig, Paul Bakaus, Fr
templates/lib/*jquery-dropmenu-*.js B 2010 Fred Heusschen
templates/lib/*jquery-validationEngine-*.js B 2010 Cedric Dugas and Olivier Refalo
templates/lib/*jquery-fineuploader-*.js B 2010 Andrew Valums
templates/lib/extra/cropperjs B 2018 Chen Fengyuan
style/600_cropper.css B 2018 Chen Fengyuan

View File

@ -2543,6 +2543,8 @@ class htmlImage extends htmlElement {
private $title;
/** onClick event */
private $onClick = null;
/** enable cropping */
private $crop = false;
/**
* Constructor.
@ -2553,7 +2555,7 @@ class htmlImage extends htmlElement {
* @param String $alt alt text (optional)
* @param String $onClick onClick code (optional)
*/
function __construct($path, $width = null, $height = null, $alt = ' ', $title = null, $onClick = null) {
public function __construct($path, $width = null, $height = null, $alt = ' ', $title = null, $onClick = null) {
$this->path = htmlspecialchars($path);
$this->width = $width;
$this->height = $height;
@ -2573,7 +2575,7 @@ class htmlImage extends htmlElement {
* @param string $scope Account type
* @return array List of input field names and their type (name => type)
*/
function generateHTML($module, $input, $values, $restricted, &$tabindex, $scope) {
public function generateHTML($module, $input, $values, $restricted, &$tabindex, $scope) {
$path = ' src="' . $this->path . '"';
$width = '';
if ($this->width != null) {
@ -2588,6 +2590,9 @@ class htmlImage extends htmlElement {
if (!empty($this->title)) {
$title = ' title="' . $this->title . '"';
}
if ($this->crop) {
$this->cssClasses[] = 'cropperjsImage';
}
$classes = '';
if (!empty($this->cssClasses)) {
$classes = 'class="' . implode(' ', $this->cssClasses) . '"';
@ -2597,9 +2602,46 @@ class htmlImage extends htmlElement {
$onClick = ' onclick="' . $this->onClick . '"';
}
echo '<img' . $path . $width . $height . $alt . $title . $classes . $onClick . ">";
if ($this->crop) {
$cropJsPath = 'templates/lib/extra/cropperjs/cropper.js';
if (is_file('../../templates/login.php')) {
$cropJsPath = '../../' . $cropJsPath;
}
else {
$cropJsPath = '../' . $cropJsPath;
}
echo '<script type="text/javascript" src="' . $cropJsPath . '"></script>';
echo '<script type="text/javascript">
var image = jQuery(\'.cropperjsImage\')[0];
var cropper = new Cropper(image, {
viewMode: 1,
movable: false,
zoomable: false,
crop: function(event) {
jQuery(\'#croppingDataX\').val(event.detail.x);
jQuery(\'#croppingDataY\').val(event.detail.y);
jQuery(\'#croppingDataWidth\').val(event.detail.width);
jQuery(\'#croppingDataHeight\').val(event.detail.height);
}
});
</script>';
echo '<input id="croppingDataX" type="hidden" name="croppingDataX" value="0"/>';
echo '<input id="croppingDataY" type="hidden" name="croppingDataY" value="0"/>';
echo '<input id="croppingDataWidth" type="hidden" name="croppingDataWidth" value="0"/>';
echo '<input id="croppingDataHeight" type="hidden" name="croppingDataHeight" value="0"/>';
}
return array();
}
/**
* Enables cropping feature.
* This will display a cropping box on the image. The cropping data
* can be found in POST data (croppingDataX, croppingDataY, croppingDataWidth, croppingDataHeight).
*/
public function enableCropping() {
$this->crop = true;
}
}
/**

View File

@ -1607,15 +1607,43 @@ class inetOrgPerson extends baseModule implements passwordService {
/**
* Sets a new photo.
*
* @return array list of error messages if any
*/
function process_photo() {
if (!isset($_POST['form_subpage_' . get_class($this) . '_attributes_submit'])) {
public function process_photo() {
if (isset($_POST['form_subpage_' . get_class($this) . '_attributes_back'])) {
return array();
}
$messages = array();
if ($this->isAdminReadOnly('jpegPhoto')) {
return array();
}
if (isset($_POST['form_subpage_' . get_class($this) . '_photo_upload'])) {
return $this->uploadPhoto();
}
if (isset($_POST['form_subpage_' . get_class($this) . '_attributes_crop'])) {
$messages = array();
$image = new Imagick();
try {
$image->readImageBlob($this->attributes['jpegPhoto'][0]);
$image->cropimage($_POST['croppingDataWidth'], $_POST['croppingDataHeight'], $_POST['croppingDataX'], $_POST['croppingDataY']);
$this->attributes['jpegPhoto'][0] = $image->getimageblob();
}
catch (Exception $e) {
$msg = $this->messages['file'][2];
$msg[] = htmlspecialchars($e->getMessage());
$messages[] = $msg;
}
return $messages;
}
}
/**
* Uploads the photo file.
*
* @return array error messages if any
*/
private function uploadPhoto() {
$messages = array();
if ($_FILES['photoFile'] && ($_FILES['photoFile']['size'] > 0)) {
$name = $_FILES['photoFile']['name'];
$extension = strtolower(substr($name, strpos($name, '.') + 1));
@ -1628,19 +1656,19 @@ class inetOrgPerson extends baseModule implements passwordService {
return array($errMsg);
}
fclose($handle);
// convert to JPG if imagick extension is available
// convert to JPG
$image = new Imagick();
try {
$image->readImageBlob($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]) ? $image->getimagewidth() : $this->moduleSettings['inetOrgPerson_jpegPhoto_maxWidth'][0];
$maxHeight = empty($this->moduleSettings['inetOrgPerson_jpegPhoto_maxHeight'][0]) ? $image->getimageheight() : $this->moduleSettings['inetOrgPerson_jpegPhoto_maxHeight'][0];
$image->thumbnailimage($maxWidth, $maxHeight, true);
}
$image->setImageCompression(Imagick::COMPRESSION_JPEG);
$image->setImageFormat('jpeg');
$data = $image->getimageblob();
$image->readImageBlob($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]) ? $image->getimagewidth() : $this->moduleSettings['inetOrgPerson_jpegPhoto_maxWidth'][0];
$maxHeight = empty($this->moduleSettings['inetOrgPerson_jpegPhoto_maxHeight'][0]) ? $image->getimageheight() : $this->moduleSettings['inetOrgPerson_jpegPhoto_maxHeight'][0];
$image->thumbnailimage($maxWidth, $maxHeight, true);
}
$image->setImageCompression(Imagick::COMPRESSION_JPEG);
$image->setImageFormat('jpeg');
$data = $image->getimageblob();
}
catch (Exception $e) {
$msg = $this->messages['file'][2];
@ -1661,14 +1689,34 @@ class inetOrgPerson extends baseModule implements passwordService {
*
* @return array meta HTML code
*/
function display_html_photo() {
public function display_html_photo() {
$container = new htmlTable();
$label = _('Photo file');
$container->addElement(new htmlTableExtendedInputFileUpload('photoFile', $label, 'photoUpload'), true);
$buttonContainer = new htmlTable();
$buttonContainer->addElement(new htmlAccountPageButton(get_class($this), 'attributes', 'submit', _('Add photo')));
$buttonContainer->addElement(new htmlAccountPageButton(get_class($this), 'attributes', 'back', _('Back')));
$container->addElement($buttonContainer);
if (empty($this->attributes['jpegPhoto'][0])) {
$container->addElement(new htmlSubTitle(_('Upload image')), true);
$label = _('Photo file');
$container->addElement(new htmlTableExtendedInputFileUpload('photoFile', $label, 'photoUpload'), true);
$container->addVerticalSpace('1rem');
$buttonContainer = new htmlTable();
$buttonContainer->addElement(new htmlAccountPageButton(get_class($this), 'photo', 'upload', _('Upload')));
$buttonContainer->addElement(new htmlAccountPageButton(get_class($this), 'attributes', 'back', _('Back')));
$container->addElement($buttonContainer);
}
else {
$container->addElement(new htmlSubTitle(_('Crop image')), true);
$jpeg_filename = 'jpg' . getRandomNumber() . '.jpg';
$outjpeg = @fopen(dirname(__FILE__) . '/../../tmp/' . $jpeg_filename, "wb");
fwrite($outjpeg, $this->attributes['jpegPhoto'][0]);
fclose ($outjpeg);
$photoFile = '../../tmp/' . $jpeg_filename;
$img = new htmlImage($photoFile);
$img->setCSSClasses(array('photo'));
$img->enableCropping();
$container->addElement($img, true);
$container->addVerticalSpace('1rem');
$buttonContainer = new htmlTable();
$buttonContainer->addElement(new htmlAccountPageButton(get_class($this), 'attributes', 'crop', _('Done')));
$container->addElement($buttonContainer, true);
}
return $container;
}

305
lam/style/600_cropper.css Normal file
View File

@ -0,0 +1,305 @@
/*!
* Cropper.js v1.3.5
* https://github.com/fengyuanchen/cropperjs
*
* Copyright (c) 2015-2018 Chen Fengyuan
* Released under the MIT license
*
* Date: 2018-04-15T06:19:56.029Z
*/
.cropper-container {
direction: ltr;
font-size: 0;
line-height: 0;
position: relative;
-ms-touch-action: none;
touch-action: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.cropper-container img {/*Avoid margin top issue (Occur only when margin-top <= -height)
*/
display: block;
height: 100%;
image-orientation: 0deg;
max-height: none !important;
max-width: none !important;
min-height: 0 !important;
min-width: 0 !important;
width: 100%;
}
.cropper-wrap-box,
.cropper-canvas,
.cropper-drag-box,
.cropper-crop-box,
.cropper-modal {
bottom: 0;
left: 0;
position: absolute;
right: 0;
top: 0;
}
.cropper-wrap-box,
.cropper-canvas {
overflow: hidden;
}
.cropper-drag-box {
background-color: #fff;
opacity: 0;
}
.cropper-modal {
background-color: #000;
opacity: .5;
}
.cropper-view-box {
display: block;
height: 100%;
outline-color: rgba(51, 153, 255, 0.75);
outline: 1px solid #39f;
overflow: hidden;
width: 100%;
}
.cropper-dashed {
border: 0 dashed #eee;
display: block;
opacity: .5;
position: absolute;
}
.cropper-dashed.dashed-h {
border-bottom-width: 1px;
border-top-width: 1px;
height: 33.33333%;
left: 0;
top: 33.33333%;
width: 100%;
}
.cropper-dashed.dashed-v {
border-left-width: 1px;
border-right-width: 1px;
height: 100%;
left: 33.33333%;
top: 0;
width: 33.33333%;
}
.cropper-center {
display: block;
height: 0;
left: 50%;
opacity: .75;
position: absolute;
top: 50%;
width: 0;
}
.cropper-center:before,
.cropper-center:after {
background-color: #eee;
content: ' ';
display: block;
position: absolute;
}
.cropper-center:before {
height: 1px;
left: -3px;
top: 0;
width: 7px;
}
.cropper-center:after {
height: 7px;
left: 0;
top: -3px;
width: 1px;
}
.cropper-face,
.cropper-line,
.cropper-point {
display: block;
height: 100%;
opacity: .1;
position: absolute;
width: 100%;
}
.cropper-face {
background-color: #fff;
left: 0;
top: 0;
}
.cropper-line {
background-color: #39f;
}
.cropper-line.line-e {
cursor: ew-resize;
right: -3px;
top: 0;
width: 5px;
}
.cropper-line.line-n {
cursor: ns-resize;
height: 5px;
left: 0;
top: -3px;
}
.cropper-line.line-w {
cursor: ew-resize;
left: -3px;
top: 0;
width: 5px;
}
.cropper-line.line-s {
bottom: -3px;
cursor: ns-resize;
height: 5px;
left: 0;
}
.cropper-point {
background-color: #39f;
height: 5px;
opacity: .75;
width: 5px;
}
.cropper-point.point-e {
cursor: ew-resize;
margin-top: -3px;
right: -3px;
top: 50%;
}
.cropper-point.point-n {
cursor: ns-resize;
left: 50%;
margin-left: -3px;
top: -3px;
}
.cropper-point.point-w {
cursor: ew-resize;
left: -3px;
margin-top: -3px;
top: 50%;
}
.cropper-point.point-s {
bottom: -3px;
cursor: s-resize;
left: 50%;
margin-left: -3px;
}
.cropper-point.point-ne {
cursor: nesw-resize;
right: -3px;
top: -3px;
}
.cropper-point.point-nw {
cursor: nwse-resize;
left: -3px;
top: -3px;
}
.cropper-point.point-sw {
bottom: -3px;
cursor: nesw-resize;
left: -3px;
}
.cropper-point.point-se {
bottom: -3px;
cursor: nwse-resize;
height: 20px;
opacity: 1;
right: -3px;
width: 20px;
}
@media (min-width: 768px) {
.cropper-point.point-se {
height: 15px;
width: 15px;
}
}
@media (min-width: 992px) {
.cropper-point.point-se {
height: 10px;
width: 10px;
}
}
@media (min-width: 1200px) {
.cropper-point.point-se {
height: 5px;
opacity: .75;
width: 5px;
}
}
.cropper-point.point-se:before {
background-color: #39f;
bottom: -50%;
content: ' ';
display: block;
height: 200%;
opacity: 0;
position: absolute;
right: -50%;
width: 200%;
}
.cropper-invisible {
opacity: 0;
}
.cropper-bg {
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC');
}
.cropper-hide {
display: block;
height: 0;
position: absolute;
width: 0;
}
.cropper-hidden {
display: none !important;
}
.cropper-move {
cursor: move;
}
.cropper-crop {
cursor: crosshair;
}
.cropper-disabled .cropper-drag-box,
.cropper-disabled .cropper-face,
.cropper-disabled .cropper-line,
.cropper-disabled .cropper-point {
cursor: not-allowed;
}

File diff suppressed because it is too large Load Diff