mirror of
https://github.com/processwire/processwire.git
synced 2025-08-13 18:24:57 +02:00
Hookable upgrades to InputfieldImage, including the ability to add thumbnail icon actions, the ability to add new buttons in image-edit mode, and full working examples in the phpdoc, plus add an EXIF working example for the image actions dropdown. This originated from PR #251 which added a Download button. But I decided we didn't really need a download button (since you can right click on image in image-edit mode and "Save as...") and instead took the additions from the PR and turned it into a hookable feature so you can add any needed actions relatively easily, whether download or something else.
Co-authored-by: JanRomero <JanRomero@users.noreply.github.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -47,6 +47,8 @@
|
||||
* @method array buildTooltipData(Pageimage $pagefile)
|
||||
* @method array getFileActions(Pagefile $pagefile)
|
||||
* @method bool|null processUnknownFileAction(Pageimage $pagefile, $action, $label)
|
||||
* @method array getImageEditButtons($pagefile, $id, $n, $buttonClass) 3.0.212+
|
||||
* @method array getImageThumbnailActions($pagefile, $id, $n, $class) 3.0.212+
|
||||
*
|
||||
*
|
||||
*/
|
||||
@@ -57,7 +59,7 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
||||
return array(
|
||||
'title' => __('Images', __FILE__), // Module Title
|
||||
'summary' => __('One or more image uploads (sortable)', __FILE__), // Module Summary
|
||||
'version' => 125,
|
||||
'version' => 126,
|
||||
'permanent' => true,
|
||||
);
|
||||
}
|
||||
@@ -682,13 +684,18 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
||||
$error = str_replace('{out}', $sanitizer->entities($thumb['error']), $this->themeSettings['error']);
|
||||
}
|
||||
$labels = $this->labels;
|
||||
$thumbnailActions = array();
|
||||
if($this->hasHook('getImageThumbnailActions()')) {
|
||||
$thumbnailActions = $this->getImageThumbnailActions($pagefile, $id, $n, 'gridImage__btn');
|
||||
}
|
||||
$thumbnailActions = implode('', $thumbnailActions);
|
||||
$out .= "
|
||||
<div class='gridImage__hover'>
|
||||
<div class='gridImage__inner'>
|
||||
<label for='' class='gridImage__trash'>
|
||||
<label for='' class='gridImage__btn gridImage__trash'>
|
||||
<input class='gridImage__deletebox' type='checkbox' name='delete_$id' value='1' title='$labels[delete]' />
|
||||
<span class='fa fa-trash-o'></span>
|
||||
</label>
|
||||
</label>$thumbnailActions
|
||||
<a class='gridImage__edit'>
|
||||
<span>$labels[edit]</span>
|
||||
</a>
|
||||
@@ -729,6 +736,37 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the image thumbnail icon actions/links/buttons
|
||||
*
|
||||
* These are icon-only actions/links displayed next to the trash icon when hovering over an image preview.
|
||||
* They are also displayed as icons on the far right side of a image when in full list mode.
|
||||
*
|
||||
* Example:
|
||||
* ~~~~~
|
||||
* $wire->addHookAfter('InputfieldImage::getImageThumbnailActions', function(HookEvent $event) {
|
||||
* $image = $event->arguments(0); // Pageimage
|
||||
* $class = $event->arguments(3); // class to use on all returned actions
|
||||
* $a = $event->return; // array
|
||||
* $a['download'] = "<a class='$class' href='$pagefile->url' download><span class='fa fa-download'></span></a>";
|
||||
* $event->return = $a;
|
||||
* });
|
||||
* ~~~~~
|
||||
*
|
||||
* #pw-hooker
|
||||
*
|
||||
* @param Pageimage $pagefile
|
||||
* @param string $id Image id string
|
||||
* @param int $n Image index number
|
||||
* @param string $class Class that should appear on all returned actions/links/buttons
|
||||
* @return array
|
||||
* @since 3.0.212
|
||||
*
|
||||
*/
|
||||
protected function ___getImageThumbnailActions($pagefile, $id, $n, $class) {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a Pageimage item
|
||||
*
|
||||
@@ -807,40 +845,70 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
||||
*
|
||||
*/
|
||||
protected function ___renderButtons($pagefile, $id, $n) {
|
||||
|
||||
if(!$this->useImageEditor) return '';
|
||||
|
||||
if($n) {} // ignore, $n is for hooks
|
||||
$buttonClass = $this->themeSettings['buttonClass'];
|
||||
$buttons = $this->getImageEditButtons($pagefile, $id, $n, $buttonClass);
|
||||
return implode('', $buttons);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array of buttons for image edit mode
|
||||
*
|
||||
* Hook after this to add or remove image edit buttons/actions.
|
||||
*
|
||||
* ~~~~~
|
||||
* // Example of adding a download button
|
||||
* $wire->addHookAfter('InputfieldImage::getImageEditButtons', function(HookEvent $event) {
|
||||
* $image = $event->arguments(0); // Pageimage
|
||||
* $class = $event->arguments(3);
|
||||
* $buttons = $event->return; // array
|
||||
* $icon = wireIconMarkup('download');
|
||||
* $buttons['download'] = "<button class='$class'><a download href='$image->url'>$icon Download</a></button>";
|
||||
* $event->return = $buttons;
|
||||
* });
|
||||
* ~~~~~
|
||||
*
|
||||
* #pw-hooker
|
||||
*
|
||||
* @param Pagefile|Pageimage $pagefile Image that buttons are for
|
||||
* @param string $id Image/file id
|
||||
* @param int $n Index of image/file (i.e 0=first)
|
||||
* @param string $buttonClass Class attribute additions that should appear on all returned <button> elements
|
||||
* @return array Array of <button> elements indexed by button name
|
||||
* @since 3.0.212
|
||||
*
|
||||
*/
|
||||
protected function ___getImageEditButtons($pagefile, $id, $n, $buttonClass) {
|
||||
|
||||
$buttons = array();
|
||||
$pageID = $pagefile->pagefiles->page->id;
|
||||
$variationCount = $pagefile->variations()->count();
|
||||
$editUrl = $this->getEditUrl($pagefile, $pageID);
|
||||
$editUrl = $this->getEditUrl($pagefile, $pageID);
|
||||
$variationUrl = $this->getVariationUrl($pagefile, $id);
|
||||
$buttonClass = $this->themeSettings['buttonClass'];
|
||||
$modalButtonClass = trim("$buttonClass $this->modalClass pw-modal");
|
||||
$modalAttrs = "data-buttons='#non_rte_dialog_buttons button' data-autoclose='1' data-close='#non_rte_cancel'";
|
||||
$labels = $this->labels;
|
||||
$out = '';
|
||||
|
||||
|
||||
// Crop
|
||||
$icon = wireIconMarkup('crop');
|
||||
$buttonText = str_replace('{out}', "$icon $labels[crop]", $this->themeSettings['buttonText']);
|
||||
$out .= "<button type='button' data-href='$editUrl' class='InputfieldImageButtonCrop $modalButtonClass' $modalAttrs>$buttonText</button>";
|
||||
|
||||
$buttons['crop'] = "<button type='button' data-href='$editUrl' class='InputfieldImageButtonCrop $modalButtonClass' $modalAttrs>$buttonText</button>";
|
||||
|
||||
// Focus
|
||||
if($this->focusMode && $this->focusMode != 'off') {
|
||||
$iconA = $pagefile->hasFocus ? 'fa-check-circle-o' : 'fa-circle-o';
|
||||
$iconB = $pagefile->hasFocus ? 'fa-check-circle' : 'fa-dot-circle-o';
|
||||
$buttonText = str_replace('{out}', "<i class='fa $iconA' data-toggle='$iconA $iconB'></i> $labels[focus]", $this->themeSettings['buttonText']);
|
||||
$out .= "<button type='button' class='InputfieldImageButtonFocus $buttonClass'>$buttonText</button>";
|
||||
$buttons['focus'] = "<button type='button' class='InputfieldImageButtonFocus $buttonClass'>$buttonText</button>";
|
||||
}
|
||||
|
||||
|
||||
// Variations
|
||||
$icon = wireIconMarkup('files-o');
|
||||
$buttonText = "$icon $labels[variations] <span class='ui-priority-secondary'>($variationCount)</span>";
|
||||
$buttonText = str_replace('{out}', $buttonText, $this->themeSettings['buttonText']);
|
||||
$out .= "<button type='button' data-href='$variationUrl' class='$modalButtonClass' data-buttons='button'>$buttonText</button>";
|
||||
|
||||
return $out;
|
||||
$buttons['variations'] = "<button type='button' data-href='$variationUrl' class='$modalButtonClass' data-buttons='button'>$buttonText</button>";
|
||||
|
||||
return $buttons;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -888,7 +956,32 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array of actions available for given Pagefile
|
||||
* Get array of actions (displayed in select dropdown) available for given Pagefile
|
||||
*
|
||||
* #pw-hooker
|
||||
*
|
||||
* ~~~~~
|
||||
* // Example of adding an “Get EXIF data” action
|
||||
* $wire->addHookAfter('InputfieldImage::getFileActions', function(HookEvent $event) {
|
||||
* $image = $event->arguments(0); // Pageimage
|
||||
* if($image->ext == 'jpg' || $image->ext == 'jpeg') {
|
||||
* $actions = $event->return; // array
|
||||
* $actions['exif'] = 'Get EXIF data';
|
||||
* $event->return = $actions;
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* // Example of handling an “Get EXIF data” action
|
||||
* $wire->addHookAfter('InputfieldImage::processUnknownFileAction', function(HookEvent $event) {
|
||||
* $image = $event->arguments(0);
|
||||
* $action = $event->arguments(1);
|
||||
* if($action === 'exif') {
|
||||
* $exif = exif_read_data($image->filename);
|
||||
* $event->warning([ "EXIF data for $image->name" => $exif ], 'icon-photo nogroup');
|
||||
* $event->return = true;
|
||||
* }
|
||||
* });
|
||||
* ~~~~~
|
||||
*
|
||||
* @param Pagefile|Pageimage $pagefile
|
||||
* @return array Associative array of ('action_name' => 'Action Label')
|
||||
@@ -1353,7 +1446,12 @@ class InputfieldImage extends InputfieldFile implements InputfieldItemList, Inpu
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an action was received that InputfieldImage does not recognize (for hooking purposes)
|
||||
* Called when a select dropdown action was received that InputfieldImage does not recognize (for hooking purposes)
|
||||
*
|
||||
* This is what should be hooked to provide the processing for a custom action added from a hook.
|
||||
* See the ___getFileActions() method documentation for full example including both hooks.
|
||||
*
|
||||
* #pw-hooker
|
||||
*
|
||||
* @param Pageimage $pagefile Image file to process
|
||||
* @param string $action Action to execute
|
||||
|
@@ -210,15 +210,18 @@ $focusPointCircleSize: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
&__trash {
|
||||
position: absolute;
|
||||
// &__trash {
|
||||
&__btn {
|
||||
// position: absolute;
|
||||
position: relative;
|
||||
float: left;
|
||||
background: white;
|
||||
padding: .2em .6em;
|
||||
color: #aaa;
|
||||
z-index: 2;
|
||||
cursor: pointer;
|
||||
|
||||
@at-root label#{&} {
|
||||
@at-root .gridImage #{&} {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -235,7 +238,7 @@ $focusPointCircleSize: 40px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
@at-root .gridImage:hover label#{&}:hover {
|
||||
@at-root .gridImage:hover #{&}:hover {
|
||||
// overwrite a:hover
|
||||
display: block;
|
||||
color: $deleteColor;
|
||||
@@ -349,7 +352,7 @@ $focusPointCircleSize: 40px;
|
||||
background: rgba($activeColor, .5);
|
||||
}
|
||||
|
||||
& .gridImage__trash {
|
||||
& .gridImage__btn {
|
||||
background-color: $activeColor;
|
||||
color: white;
|
||||
|
||||
@@ -358,7 +361,7 @@ $focusPointCircleSize: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .gridImage__trash:hover {
|
||||
&:hover .gridImage__btn:hover {
|
||||
background-color: $activeColor;
|
||||
}
|
||||
}
|
||||
@@ -390,6 +393,7 @@ $focusPointCircleSize: 40px;
|
||||
|
||||
&:hover .gridImage__trash:hover {
|
||||
background-color: $deleteColor;
|
||||
color: white;
|
||||
}
|
||||
|
||||
&:hover .gridImage__inner {
|
||||
@@ -566,6 +570,9 @@ $focusPointCircleSize: 40px;
|
||||
margin: 1em 0 0.5em 0;
|
||||
button {
|
||||
margin-bottom: 0.5em;
|
||||
a, a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -828,11 +835,13 @@ $focusPointCircleSize: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
&__trash {
|
||||
&__btn {
|
||||
z-index: 5;
|
||||
display: block !important;
|
||||
top: 0;
|
||||
right: 0;
|
||||
float: right;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&__edit {
|
||||
@@ -874,7 +883,10 @@ $focusPointCircleSize: 40px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover .gridImage__trash:hover {
|
||||
.gridImage__btn {
|
||||
color: white;
|
||||
}
|
||||
&:hover .gridImage__btn:hover {
|
||||
color: white;
|
||||
background-color: $deleteColor;
|
||||
}
|
||||
@@ -934,4 +946,4 @@ $focusPointCircleSize: 40px;
|
||||
.gridImage__edit { display:block !important; }
|
||||
.gridImage__edit span { display:none; }
|
||||
.gridImage__edit:hover span { display:inline; }
|
||||
*/
|
||||
*/
|
||||
|
Reference in New Issue
Block a user