added cropping support
This commit is contained in:
parent
5ec89293b2
commit
137ef0721f
|
@ -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-dropmenu-*.js B 2010 Fred Heusschen
|
||||||
templates/lib/*jquery-validationEngine-*.js B 2010 Cedric Dugas and Olivier Refalo
|
templates/lib/*jquery-validationEngine-*.js B 2010 Cedric Dugas and Olivier Refalo
|
||||||
templates/lib/*jquery-fineuploader-*.js B 2010 Andrew Valums
|
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
|
||||||
|
|
||||||
|
|
|
@ -2543,6 +2543,8 @@ class htmlImage extends htmlElement {
|
||||||
private $title;
|
private $title;
|
||||||
/** onClick event */
|
/** onClick event */
|
||||||
private $onClick = null;
|
private $onClick = null;
|
||||||
|
/** enable cropping */
|
||||||
|
private $crop = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
|
@ -2553,7 +2555,7 @@ class htmlImage extends htmlElement {
|
||||||
* @param String $alt alt text (optional)
|
* @param String $alt alt text (optional)
|
||||||
* @param String $onClick onClick code (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->path = htmlspecialchars($path);
|
||||||
$this->width = $width;
|
$this->width = $width;
|
||||||
$this->height = $height;
|
$this->height = $height;
|
||||||
|
@ -2573,7 +2575,7 @@ class htmlImage extends htmlElement {
|
||||||
* @param string $scope Account type
|
* @param string $scope Account type
|
||||||
* @return array List of input field names and their type (name => 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 . '"';
|
$path = ' src="' . $this->path . '"';
|
||||||
$width = '';
|
$width = '';
|
||||||
if ($this->width != null) {
|
if ($this->width != null) {
|
||||||
|
@ -2588,6 +2590,9 @@ class htmlImage extends htmlElement {
|
||||||
if (!empty($this->title)) {
|
if (!empty($this->title)) {
|
||||||
$title = ' title="' . $this->title . '"';
|
$title = ' title="' . $this->title . '"';
|
||||||
}
|
}
|
||||||
|
if ($this->crop) {
|
||||||
|
$this->cssClasses[] = 'cropperjsImage';
|
||||||
|
}
|
||||||
$classes = '';
|
$classes = '';
|
||||||
if (!empty($this->cssClasses)) {
|
if (!empty($this->cssClasses)) {
|
||||||
$classes = 'class="' . implode(' ', $this->cssClasses) . '"';
|
$classes = 'class="' . implode(' ', $this->cssClasses) . '"';
|
||||||
|
@ -2597,9 +2602,46 @@ class htmlImage extends htmlElement {
|
||||||
$onClick = ' onclick="' . $this->onClick . '"';
|
$onClick = ' onclick="' . $this->onClick . '"';
|
||||||
}
|
}
|
||||||
echo '<img' . $path . $width . $height . $alt . $title . $classes . $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();
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1607,15 +1607,43 @@ class inetOrgPerson extends baseModule implements passwordService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a new photo.
|
* Sets a new photo.
|
||||||
|
*
|
||||||
|
* @return array list of error messages if any
|
||||||
*/
|
*/
|
||||||
function process_photo() {
|
public function process_photo() {
|
||||||
if (!isset($_POST['form_subpage_' . get_class($this) . '_attributes_submit'])) {
|
if (isset($_POST['form_subpage_' . get_class($this) . '_attributes_back'])) {
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
$messages = array();
|
|
||||||
if ($this->isAdminReadOnly('jpegPhoto')) {
|
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;
|
return $messages;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads the photo file.
|
||||||
|
*
|
||||||
|
* @return array error messages if any
|
||||||
|
*/
|
||||||
|
private function uploadPhoto() {
|
||||||
|
$messages = array();
|
||||||
if ($_FILES['photoFile'] && ($_FILES['photoFile']['size'] > 0)) {
|
if ($_FILES['photoFile'] && ($_FILES['photoFile']['size'] > 0)) {
|
||||||
$name = $_FILES['photoFile']['name'];
|
$name = $_FILES['photoFile']['name'];
|
||||||
$extension = strtolower(substr($name, strpos($name, '.') + 1));
|
$extension = strtolower(substr($name, strpos($name, '.') + 1));
|
||||||
|
@ -1628,7 +1656,7 @@ class inetOrgPerson extends baseModule implements passwordService {
|
||||||
return array($errMsg);
|
return array($errMsg);
|
||||||
}
|
}
|
||||||
fclose($handle);
|
fclose($handle);
|
||||||
// convert to JPG if imagick extension is available
|
// convert to JPG
|
||||||
$image = new Imagick();
|
$image = new Imagick();
|
||||||
try {
|
try {
|
||||||
$image->readImageBlob($data);
|
$image->readImageBlob($data);
|
||||||
|
@ -1661,14 +1689,34 @@ class inetOrgPerson extends baseModule implements passwordService {
|
||||||
*
|
*
|
||||||
* @return array meta HTML code
|
* @return array meta HTML code
|
||||||
*/
|
*/
|
||||||
function display_html_photo() {
|
public function display_html_photo() {
|
||||||
$container = new htmlTable();
|
$container = new htmlTable();
|
||||||
|
if (empty($this->attributes['jpegPhoto'][0])) {
|
||||||
|
$container->addElement(new htmlSubTitle(_('Upload image')), true);
|
||||||
$label = _('Photo file');
|
$label = _('Photo file');
|
||||||
$container->addElement(new htmlTableExtendedInputFileUpload('photoFile', $label, 'photoUpload'), true);
|
$container->addElement(new htmlTableExtendedInputFileUpload('photoFile', $label, 'photoUpload'), true);
|
||||||
|
$container->addVerticalSpace('1rem');
|
||||||
$buttonContainer = new htmlTable();
|
$buttonContainer = new htmlTable();
|
||||||
$buttonContainer->addElement(new htmlAccountPageButton(get_class($this), 'attributes', 'submit', _('Add photo')));
|
$buttonContainer->addElement(new htmlAccountPageButton(get_class($this), 'photo', 'upload', _('Upload')));
|
||||||
$buttonContainer->addElement(new htmlAccountPageButton(get_class($this), 'attributes', 'back', _('Back')));
|
$buttonContainer->addElement(new htmlAccountPageButton(get_class($this), 'attributes', 'back', _('Back')));
|
||||||
$container->addElement($buttonContainer);
|
$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;
|
return $container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
Loading…
Reference in New Issue