'Page Edit Image', 'summary' => 'Provides an image select capability as used by some Fieldtype modules (like TinyMCE)', 'version' => 108, 'permanent' => true, 'permission' => 'page-edit', ); } protected $maxImageWidth = 835; protected $page = null; protected $editorPage = null; protected $defaultClass = 'Pageimage'; protected $file = ''; protected $editWidth = 0; protected $editHeight = 0; protected $extensions = 'jpg|jpeg|gif|png|svg'; protected static $defaultConfig = array( 'skipFields' => '', 'noSizeAttrs' => 0, 'alignLeftClass' => 'align_left', 'alignRightClass' => 'align_right', 'alignCenterClass' => 'align_center' ); public function __construct() { foreach(self::$defaultConfig as $key => $value) $this->set($key, $value); } public function init() { if($this->config->demo) throw new WireException("Sorry, image editing functions are disabled in demo mode"); $this->modules->get("ProcessPageList"); $id = (int) $this->input->get->id; $editID = (int) $this->input->get->edit_page_id; if($editID) { $this->wire('session')->set($this, 'edit_page_id', $editID); } else { $editID = (int) $this->wire('session')->get($this, 'edit_page_id'); } if($editID) { $this->editorPage = $this->wire('pages')->get($editID); if(!$this->editorPage->editable()) { $this->editorPage = null; $this->wire('session')->remove($this, 'edit_page_id'); } } // if no ID was specified, then retrieive ID from filename path, if it's there if($this->input->get->file && preg_match('{[/,]}', $this->input->get->file)) { if(preg_match('{(\d+)[/,][^/,]+\.(' . $this->extensions . ')$}iD', $this->input->get->file, $matches)) { // ..........ID.../,filename.ext // format: 123/filename.jpg OR 123,filename.jpg // also covers new pagefileSecure URLs $id = (int) $matches[1]; } else if(preg_match('{(/[\d/]+)/([-_.a-z0-9]+)\.(' . $this->extensions . ')$}iD', $this->input->get->file, $matches)) { // .................ID........filename........ext // extended asset path format: 1/2/3/filename.jpg $id = PagefilesManager::dirToPageID($matches[1]); } else if(preg_match('{^' . wire('config')->urls->root . '([-_./a-zA-Z0-9]*/)' . wire('config')->pagefileUrlPrefix . '[^/]+\.(' . $this->extensions . ')$}iD', $this->input->get->file, $matches)) { // .............................../....................path/to/page.........................-?....................filename..........ext // legacy pagefileSecure URL format: /path/to/page/filename.jpg // @todo: does this still need to be here or can it be dropped? $this->page = wire('pages')->get('/' . $matches[1]); $id = $this->page->id; } } if(!$id) throw new WireException("No page specified"); if(!$this->page) $this->page = $this->pages->get($id); if(!$this->page) throw new WireException("No page specified"); if(!$this->page->id) throw new WireException("Unknown page"); if(!$this->editorPage) $this->editorPage = $this->page; // note we use hasPermission('page-view') rather than viewable() here becuase we want to allow pages without template files if(!$this->user->hasPermission('page-view', $this->page)) throw new WireException("You do not have access to images on this page"); if($this->input->get->winwidth) $this->maxImageWidth = ((int) $this->input->get->winwidth) - 70; if($this->maxImageWidth < 400) $this->maxImageWidth = 400; parent::init(); } public function getPageimage() { $images = $this->getImages($this->page); $filename = basename($this->input->get->file); if(strpos($filename, ',') === false) $filename = $this->page->id . ',' . $filename; // prepend ID if it's not there, needed for ajax in-editor resize if(!preg_match('/\.(' . $this->extensions . ')$/iD', $filename, $matches)) throw new WireException("Unknown image file"); // get the original, non resized version, if present if(preg_match('/(\.(\d+)x(\d+)(-[-_a-z0-9]+)?)\.' . $matches[1] . '$/', $filename, $matches)) { // original.600x400-suffix1-suffix2.ext $this->editWidth = (int) $matches[2]; $this->editHeight = (int) $matches[3]; $filename = str_replace($matches[1], '', $filename); // remove dimensions and optional suffix } if(!array_key_exists($filename, $images)) throw new WireException("Invalid image file: $filename"); return $images[$filename]; } public function getImages(Page $page, $level = 0) { $allImages = array(); $numImages = 0; $numImageFields = 0; $skipFields = explode(' ', $this->skipFields); if(!$page->id) return $allImages; foreach($page->fields as $field) { if(in_array($field->name, $skipFields)) continue; if($field->type instanceof FieldtypeRepeater) { // get images that are possibly in a repeater foreach($page->get($field->name) as $p) { $images = $this->getImages($p, $level+1); if(!count($images)) continue; $allImages = array_merge($allImages, $images); $numImages += count($images); $numImageFields++; } continue; } if(!$field->type instanceof FieldtypeImage) continue; $numImageFields++; $images = $page->get($field->name); if(!count($images)) continue; foreach($images as $image) { $numImages++; $key = $page->id . ',' . $image->basename; // page_id,basename for repeater support $allImages[$key] = $image; } } if(!$level) { if(!$numImageFields) $this->message($this->_("There are no image fields on this page. Choose another page to select images from.")); // Message when page has no image fields else if(!$numImages) $this->message($this->_("There are no images present on this page. Close this window and upload images, or select images from another page.")); // Message when page has no images } return $allImages; } public function ___execute() { if($this->config->demo) throw new WireException("Sorry, image editing functions are disabled in demo mode"); if($this->input->get->file) return $this->executeEdit(); $images = $this->getImages($this->page); $out = ''; if(count($images)) { $winwidth = (int) $this->input->get->winwidth; foreach($images as $image) { $width = $image->width(); if($width > $this->maxImageWidth) $width = $this->maxImageWidth; $out .= "\n\t
  • " . "\"{$image-description}\" />
  • "; } $out = "\n"; } $form = $this->modules->get("InputfieldForm"); $form->action = "./"; $form->method = "get"; $field = $this->modules->get("InputfieldPageListSelect"); $field->label = $this->_("Images on Page:") . ' ' . $this->page->get("title") . " (" . $this->page->path . ")"; // Headline for page selection, precedes current page title/url $field->description = $this->_("If you would like to select images from another page, select the page below."); // Instruction on how to select another page $field->attr('id+name', 'page_id'); $field->value = $this->page->id; $field->parent_id = 0; $field->collapsed = count($images) ? Inputfield::collapsedYes : Inputfield::collapsedNo; $field->required = true; $form->append($field); $out = $form->render() . $out; return "
    " . $out . "\n
    "; } public function ___executeEdit() { $attrs = array( 'class' => '', 'width' => 0, 'height' => 0, ); $image = $this->getPageimage(); if(!is_file($image->filename)) throw new WireException("Image file does not exist"); $basename = $image->basename; $fullname = $image->page->id . ',' . $basename; $originalWidth = $image->width(); $originalHeight = $image->height(); $width = (int) $this->input->get->width; $height = (int) $this->input->get->height; if(!$width) $width = $this->editWidth; if(!$width) $width = ''; if(!$height) $height = $this->editHeight; if(!$height) $height = ''; // if they aren't already working with a resized image, and it's being scaled down, // then add the 'resized' class to ensure that our pwimage plugin knows to perform the resize if(basename($this->input->get->file) == $fullname && $originalWidth > $width) $attrs['class'] .= " resized"; $out = "\n
    " . "\n\t

    " . "\n\t\t" . htmlentities($basename, ENT_QUOTES, 'UTF-8') . "  •  " . "\n\t\t" . "\n\t\t\t  " . "\n\t\t\t" . "\n\t\t  " . "\n\t\t" . $this->_("You can also drag the lower right corner of the image to resize.") . "" . "\n\t

    "; $description = isset($_GET['description']) ? $this->input->get->description : $image->description; if(strlen($description) > 1024) $description = substr($description, 0, 1024); $description = htmlentities($description, ENT_QUOTES, "UTF-8"); $descriptionLabel = $this->_('Description'); $out .= "\n\t

    " . "\n\t\t" . "\n\t\t" . "\n\t

    "; $out .= "\n\t

    " . "\n\t\t" . "\n\t

    "; if(!$width && $image->width() > $this->maxImageWidth) { $width = $this->maxImageWidth; $height = floor(($width / $image->width()) * $image->height()); // $height = 0; } if($width) $attrs['width'] = $width; if($width && $height) $attrs['height'] = $height; if($width != $originalWidth) { $imgURL = str_replace($this->wire('config')->urls->root, '/', $image->url); $imgLink = trim($this->input->get->link); if($imgLink) { $test = substr($imgLink, -1 * strlen($imgURL)); $checked = $test == $imgURL ? " checked='checked' data-was-checked='1'" : ""; } else { $checked = ''; } $out .= "\n\t"; } $out .= "\n\t"; $out .= "\n
    "; $attrStr = ''; foreach($attrs as $key => $value) if($value) $attrStr .= "$key='" . trim($value) . "' "; $nosize = $this->noSizeAttrs ? 1 : ''; $out .= "\n\t
    " . "\n\t\t" . "\n\t
    "; return $out; } public function ___executeResize() { $width = (int) $this->input->get->width; $height = (int) $this->input->get->height; $class = $this->sanitizer->name($this->input->get->class); $image = $this->getPageimage(); if($width < $image->width) { $suffix = array('is'); // is=image select if($this->editorPage && $this->editorPage->id != $this->page->id) $suffix[] = "pid$this->editorPage"; // identify page that is using the variation $resized = $image->width($width, array('suffix' => $suffix)); $height = $resized->height(); } else { $width = $image->width; $height = $image->height; $resized = $image; } // note IE8 won't properly read the width/height attrs via ajax // so we provide the width/height in separate fields $nosize = $this->noSizeAttrs ? 1 : ''; $out = "

    " . "$widthx" . "$height
    " . "" . "

    "; return $out; } static public function getModuleConfigInputfields(array $data) { $inputfields = new InputfieldWrapper(); $data = array_merge(self::$defaultConfig, $data); $f = wire('modules')->get('InputfieldText'); $f->attr('name', 'skipFields'); $f->attr('value', isset($data['skipFields']) ? $data['skipFields'] : ''); $f->label = __('Field names to skip for selection'); $f->description = __('Enter the names of any image fields (separated by a space) that you do not want to allow for selection with this module.'); $inputfields->add($f); $f = wire('modules')->get('InputfieldCheckbox'); $f->attr('name', 'noSizeAttrs'); $f->attr('value', 1); if(!empty($data['noSizeAttrs'])) $f->attr('checked', 'checked'); $f->label = __('Skip width and height attributes on image tags?', __FILE__); // noSizeAttr label $f->description = __('By default, this module will include width and height attributes in the tag. If you are using responsive images, you might want to disable this behavior. Check the box to disable width and height attributes.', __FILE__); // noSizeAttr description $inputfields->add($f); $f = wire('modules')->get('InputfieldText'); $f->attr('name', 'alignLeftClass'); $f->attr('value', $data['alignLeftClass']); $f->label = __('Align Image Left Class'); $inputfields->add($f); $f = wire('modules')->get('InputfieldText'); $f->attr('name', 'alignRightClass'); $f->attr('value', $data['alignRightClass']); $f->label = __('Align Image Right Class'); $inputfields->add($f); $f = wire('modules')->get('InputfieldText'); $f->attr('name', 'alignCenterClass'); $f->attr('value', $data['alignCenterClass']); $f->label = __('Align Image Center Class'); $inputfields->add($f); return $inputfields; } }