Server IP : 195.201.23.43 / Your IP : 18.223.122.53 Web Server : Apache System : Linux webserver2.vercom.be 5.4.0-192-generic #212-Ubuntu SMP Fri Jul 5 09:47:39 UTC 2024 x86_64 User : kdecoratie ( 1041) PHP Version : 7.1.33-63+ubuntu20.04.1+deb.sury.org+1 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals, MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /home/kdecoratie/public_html/plugins/content/sigplus/ |
Upload File : |
<?php /** * @file * @brief sigplus Image Gallery Plus plug-in for Joomla * @author Levente Hunyadi * @version 1.4.2 * @remarks Copyright (C) 2009-2011 Levente Hunyadi * @remarks Licensed under GNU/GPLv3, see http://www.gnu.org/licenses/gpl-3.0.html * @see http://hunyadi.info.hu/projects/sigplus */ /* * sigplus Image Gallery Plus plug-in for Joomla * Copyright 2009-2010 Levente Hunyadi * * sigplus is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * sigplus is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ // no direct access defined( '_JEXEC' ) or die( 'Restricted access' ); if (!defined('SIGPLUS_VERSION')) { define('SIGPLUS_VERSION', '1.4.2'); } if (!defined('SIGPLUS_DEBUG')) { define('SIGPLUS_DEBUG', false); } if (!defined('SIGPLUS_LOGGING')) { define('SIGPLUS_LOGGING', false); } if (!defined('SIGPLUS_CONTENT_CACHING')) { define('SIGPLUS_CONTENT_CACHING', true); } if (!defined('SIGPLUS_DISABLE_TITLE')) { define('SIGPLUS_DISABLE_TITLE', false); } require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'exception.php'; require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'params.php'; require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'services.php'; require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'thumbs.php'; require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'engines.php'; function array_value($array, $key) { return isset($array[$key]) ? $array[$key] : false; } /** * Builds HTML from tag name, attribute array and element content. */ function make_html($element, $attrs = false, $content = false) { $html = '<'.$element; if ($attrs !== false) { foreach ($attrs as $key => $value) { if ($value !== false) { $html .= ' '.$key.'="'.htmlspecialchars($value).'"'; } } } if ($content !== false) { $html .= '>'.$content.'</'.$element.'>'; } else { $html .= '/>'; } return $html; } /** * Returns the href attribute value of an anchor element. */ function get_anchor_attrs($html) { $matches = array(); if (!preg_match('#<a\s([^<>]*)>#u', $html, $matches)) { return false; } $attrs = string_to_array(html_entity_decode($matches[1], ENT_QUOTES, 'UTF-8')); return $attrs; } /** * Logging services. */ class SIGPlusLogging { /** Error log. */ private $log = array(); /** Whether interactive javascript to show/hide listings has been added to the page head. */ private $script = false; /** Singleton instance. */ private static $inst = false; public static function instance() { if (self::$inst === false) { self::$inst = new SIGPlusLogging(); } return self::$inst; } public function append($message) { $this->log[] = $message; } public function appendblock($message, $block) { $this->log[] = $message.' <a href="#" class="sigplus-logging">Show</a><pre class="sigplus-logging">'.htmlspecialchars($block).'</pre>'; } public function fetch() { if (!$this->script) { $this->script = true; $document = JFactory::getDocument(); $document->addScriptDeclaration('window.addEvent("domready", function () { var links = $$("a.sigplus-logging"); links.addEvent("click", function (event) { var el = $(this).getNext(); el.setStyle("display", el.getStyle("display") != "none" ? "none" : "block"); event.preventDefault(); }); links.getNext().setStyle("display", "none"); });'); } ob_start(); print '<ul>'; foreach ($this->log as $logentry) { print '<li>'.$logentry.'</li>'; } print '</ul>'; $this->log = array(); return ob_get_clean(); } } class SIGPlusCoreConfiguration { /** Whether to utilize a content delivery network to load javascript frameworks. */ public $ajaxapi = 'default'; /** Whether to enter debug mode. */ public $debug = false; public function validate() { if (is_string($this->ajaxapi)) { switch ($this->ajaxapi) { case 'none': case 'local': case 'cdn-google': case 'cdn-microsoft': case 'cdn-jquery': break; default: $this->ajaxapi = 'default'; } } else { $this->ajaxapi = (bool) $this->ajaxapi ? 'default' : 'local'; } $this->debug = (bool) $this->debug; } /** * Set parameter object from a Joomla JParameters object. */ public function setParameters($params) { $this->ajaxapi = self::getParameterValue($params, 'ajaxapi', 'default'); $this->debug = self::getParameterValue($params, 'debug', false); $this->validate(); } private static function getParameterValue($params, $name, $default) { if ($params instanceof stdClass) { if (isset($params->$name)) { return $params->$name; } } else if ($params instanceof JRegistry) { // Joomla 2.5 and earlier $paramvalue = $params->get($name); if (isset($paramvalue)) { return $paramvalue; } } return $default; } } /** * System-wide global configuration settings. */ class SIGPlusConfiguration { public $core; public $services; public $galleries; public function setConfiguration(SIGPlusCoreConfiguration $core, SIGPlusImageServicesConfiguration $services, SIGPlusGalleryParameters $galleries) { $this->core = $core; $this->services = $services; $this->galleries = $galleries; } public function setParameters($params) { $this->core = new SIGPlusCoreConfiguration(); // global settings $this->core->setParameters($params); $this->services = new SIGPlusImageServicesConfiguration(); // image service settings $this->services->setParameters($params); $this->galleries = new SIGPlusGalleryParameters(); // administration back-end parameters $this->galleries->setParameters($params); } } /** * sigplus Image Gallery Plus service class. */ class SIGPlusCore { /** General parameters. */ private $imageservices; /** Associative array of default gallery-specific parameters. */ private $defparams; /** Associative array of current gallery-specific parameters. */ private $curparams; /** A list of identifiers issued. The list ensures uniqueness: duplicate identifiers are decorated to make them unique. */ private static $galleryids = array(); public function __construct(SIGPlusConfiguration $configuration = null) { // set general parameters $engineservices = SIGPlusEngineServices::instance(); $conf = is_null($configuration) ? new SIGPlusCoreConfiguration() : $configuration->core; $engineservices->ajaxapi = $conf->ajaxapi; if (SIGPLUS_DEBUG) { // force debug mode $engineservices->debug = true; } elseif (!isset($engineservices->debug)) { $engineservices->debug = (bool) $conf->debug; // do not disable debug mode if already set } // set default global parameters for image galleries $this->defparams = is_null($configuration) ? new SIGPlusGalleryParameters() : $configuration->galleries; if (SIGPLUS_LOGGING) { $logging = SIGPlusLogging::instance(); $logging->appendblock('Global parameters are:', print_r($this->defparams, true)); } // create image services object try { $this->imageservices = new SIGPlusImageServices(is_null($configuration) ? null : $configuration->services); } catch (Exception $e) { $this->imageservices = null; // image services not available throw $e; // re-throw exception } } /** * Creates a thumbnail image, a preview image, and a watermarked image for an original. * Images are generated only if they do not already exist. * A separate thumbnail image is generated if the preview is too large to act as a thumbnail. * @param string $imageref An absolute file system path or URL to an image. */ private function createPreviewImage($imageref) { $params = new SIGPlusPreviewParameters($this->curparams); $imagelibrary = SIGPlusImageLibrary::instantiate($this->imageservices->getLibrary()); // create watermarked image if (!is_remote_path($imageref) && $this->curparams->watermark && ($watermarkpath = $this->imageservices->checkWatermarkPath(dirname($imageref))) !== false) { if ($this->imageservices->checkWatermarkedPath($imageref) === false) { $watermarkedpath = $this->imageservices->createWatermarkedPath($imageref); $watermarkparams = $this->curparams->watermark_params; $watermarkparams['quality'] = $params->quality; // GD cannot extract quality parameter from stored image, use quality set by user $result = $imagelibrary->createWatermarked($imageref, $watermarkpath, $watermarkedpath, $watermarkparams); if (SIGPLUS_LOGGING) { $logging = SIGPlusLogging::instance(); if ($result) { $logging->append('Saved watermarked image to <kbd>'.$watermarkedpath.'</kbd>'); } else { $logging->append('Failed to save watermarked image to <kbd>'.$watermarkedpath.'</kbd>'); } } } } // create preview image if ($this->imageservices->checkPreviewPath($imageref, $params) === false) { // create image on-the-fly if not exists $previewpath = $this->imageservices->createPreviewPath($imageref, $params); $result = $imagelibrary->createThumbnail($imageref, $previewpath, $params->width, $params->height, $params->crop, $params->quality); if (SIGPLUS_LOGGING) { $logging = SIGPlusLogging::instance(); if ($result) { $logging->append('Saved preview image to <kbd>'.$previewpath.'</kbd>'); } else { $logging->append('Failed to save preview image to <kbd>'.$previewpath.'</kbd>'); } } } // create thumbnail image if ($this->imageservices->checkThumbnailPath($imageref, $params) === false) { // separate thumbnail image is required $thumbpath = $this->imageservices->createThumbnailPath($imageref, $params); $thumbparams = $params->getThumbnailParameters(); if (!isset($previewpath)) { // preview image already exists but not thumbnail $previewpath = $this->imageservices->createPreviewPath($imageref, $params); } $result = $imagelibrary->createThumbnail($previewpath, $thumbpath, $thumbparams->width, $thumbparams->height, $thumbparams->crop, $thumbparams->quality); // use preview image as source if (SIGPLUS_LOGGING) { $logging = SIGPlusLogging::instance(); if ($result) { $logging->append('Saved thumbnail to <kbd>'.$thumbpath.'</kbd>'); } else { $logging->append('Failed to save thumbnail to <kbd>'.$thumbpath.'</kbd>'); } } } } /** * Fetches metadata associated with a (local) image. */ private function getImageMetadata($imageref) { if (is_remote_path($imageref)) { return false; // do not extract metadata of remote images } else { require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'metadata.php'; return SIGPlusIPTCServices::getImageMetadata($imageref); } } /** * Retrieves all data associated with an image. * @param string $imageref An absolute file system path or URL to an image. * @return An associative array of image (meta)data. */ private function getImageData($imageref, $index, $label = false) { // get lightbox $engineservices = SIGPlusEngineServices::instance(); $lightbox = $engineservices->getLightboxEngine($this->curparams->lightbox); // get selected lightbox engine if any or use default // avoid generating HTML for those images that cannot be accessed if ($this->curparams->maxcount > 0 && $index >= $this->curparams->maxcount && !($lightbox && $lightbox->isQuickNavigationSupported())) { return false; } // get image thumbnail URL and parameters $params = new SIGPlusPreviewParameters($this->curparams); $thumburl = $this->imageservices->getThumbnailUrl($imageref, $params); $previewurl = $this->imageservices->getPreviewUrl($imageref, $params); // use caption and summary from labels file if available if ($label !== false) { $caption = $label->getCaptionHtml(); $summary = $label->getDescriptionHtml(); } else { $caption = false; $summary = false; } // try to fill in missing caption or summary from image metadata if (!$caption || !$summary) { $metadata = $this->getImageMetadata($imageref); if ($metadata !== false) { // use caption and summary from metadata if (!$caption && isset($metadata['Headline'])) { if (is_array($metadata['Headline'])) { $caption = implode(';', $metadata['Headline']); } else { $caption = $metadata['Headline']; } if (!$caption && isset($metadata['Title'])) { $caption = $metadata['Title']; } // make caption suitable for embedding in HTML $caption = htmlspecialchars($caption); } if (!$summary && isset($metadata['Caption-Abstract'])) { if (is_array($metadata['Caption-Abstract'])) { $summary = implode(';', $metadata['Caption-Abstract']); } else { $summary = $metadata['Caption-Abstract']; } $summary = htmlspecialchars($summary); } } } // get image metadata (for display in metadata window) if not yet processed if ($this->curparams->metadata && !isset($metadata)) { $metadata = $this->getImageMetadata($imageref); } // get target URL for preview image $url = false; if ($lightbox) { // display lightbox pop-up window when thumbnail is clicked if (is_remote_path($imageref)) { $url = $imageref; } else { $url = $this->imageservices->getImageUrl($imageref, $this->curparams->authentication); if ($this->curparams->watermark && $this->imageservices->checkWatermarkedPath($imageref) !== false) { $url = $this->imageservices->getWatermarkedUrl($imageref); } } $anchor_attrs = array('href' => $url); } elseif ($summary && ($anchor = get_anchor_attrs($summary)) !== false || $this->curparams->defdescription && ($anchor = get_anchor_attrs($this->curparams->defdescription)) !== false) { // check if there is a hyperlink in the description and use it as target link $anchor_attrs = $anchor; } // get preview image parameters $img_attrs = array('preview' => $previewurl); if (!$this->curparams->crop) { if (($previewpath = $this->imageservices->checkPreviewPath($imageref, $params)) !== false) { $imagedims = getimagesize($previewpath); } else { $imagedims = false; } if ($imagedims !== false) { $params->width = $imagedims[0]; $params->height = $imagedims[1]; } } $img_attrs['width'] = $params->width; $img_attrs['height'] = $params->height; // get thumbnail image parameters if ($thumburl != $previewurl) { $img_attrs['thumb'] = $thumburl; $thumbparams = $params->getThumbnailParameters(); $img_attrs['thumb-width'] = $thumbparams->width; $img_attrs['thumb-height'] = $thumbparams->height; } // set image caption and summary if ($caption) { $img_attrs['caption'] = $caption; } if ($summary) { $img_attrs['summary'] = $summary; } // get download URL if ($this->curparams->download && ($downloadurl = $this->imageservices->getFullsizeImageDownloadUrl($imageref, $this->curparams->authentication)) !== false) { $img_attrs['fullsize'] = $downloadurl; } if (SIGPLUS_LOGGING) { $logging = SIGPlusLogging::instance(); $logging->append('Preview image URL is <kbd>'.$previewurl.'</kbd>'); if ($thumburl != $previewurl) { $logging->append('Thumbnail image URL is <kbd>'.$thumburl.'</kbd>'); } if (isset($metadata)) { if ($metadata !== false) { $logging->append('Image metadata is available.'); } else { $logging->append('Image metadata has been processed but is not available.'); } } } $imagedata = array( 'image' => $img_attrs); if (isset($anchor_attrs)) { $imagedata['anchor'] = $anchor_attrs; } if ($this->curparams->metadata && isset($metadata) && $metadata !== false) { $imagedata['metadata'] = $metadata; } return $imagedata; } /** * Returns JavaScript code for a preview image in a gallery list. * @param integer $index The zero-based index of the image in the gallery. * @param integer $total The total number of images in the gallery. * @return string JavaScript code. */ private function getPreviewScript($galleryid, $index, $total, $imagedata) { $anchor_attrs = isset($imagedata['anchor']) ? $imagedata['anchor'] : false; $img_attrs = $imagedata['image']; return '['.implode(',', array( ($anchor_attrs ? json_encode($anchor_attrs['href']) : 'null'), json_encode(isset($img_attrs['preview']) ? $img_attrs['preview'] : ''), $img_attrs['width'], $img_attrs['height'], json_encode(isset($img_attrs['thumb']) ? $img_attrs['thumb'] : ''), // only with progressive loading enabled json_encode(isset($img_attrs['caption']) ? $img_attrs['caption'] : ''), json_encode(isset($img_attrs['summary']) ? $img_attrs['summary'] : ''), json_encode(isset($img_attrs['fullsize']) ? $img_attrs['fullsize'] : ''), json_encode(isset($imagedata['metadata']) ? $imagedata['metadata'] : null) ) ).']'; } /** * Returns HTML code for a preview image in a gallery list. * @param integer $index The zero-based index of the image in the gallery. * @param integer $total The total number of images in the gallery. * @return string HTML code. */ private function getPreviewHtml($galleryid, $index, $total, $imagedata) { ob_start(); if (isset($imagedata['anchor'])) { $anchor_attrs = $imagedata['anchor']; } $img_params = $imagedata['image']; $engineservices = SIGPlusEngineServices::instance(); $lightbox = $engineservices->getLightboxEngine($this->curparams->lightbox); // get selected lightbox engine if any or use default // add rel attribute to hook lightbox if ($lightbox && isset($anchor_attrs)) { $anchor_attrs['rel'] = $lightbox->getLinkAttribute($galleryid); } // compose preview image (HTML img element) $imageid = $galleryid.'_img'.sprintf('%04d', $index); $img_attrs = array( 'id' => $imageid, 'width' => $img_params['width'], 'height' => $img_params['height'], 'src' => $img_params['preview']); if (isset($img_params['thumb'])) { // a separate thumbnail image is available //$img_attrs['data-thumb'] = $img_params['thumb']; if ($this->curparams->maxcount > 0 && $index >= $this->curparams->maxcount) { // preview image would never be shown, image would only appear in lightbox navigation bar... $img_attrs['longdesc'] = $img_params['thumb']; // ...but lightbox can extract URL from longdesc attribute $img_attrs['src'] = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw=='; // fully transparent blank 1x1 image } elseif ($this->curparams->progressive) { // a slider is present with progressive loading enabled $img_attrs['longdesc'] = $img_attrs['src']; $img_attrs['src'] = $img_params['thumb']; // overriden with preview image by progressive loading } } if (!isset($img_params['caption']) && $this->curparams->deftitle !== false) { $img_params['caption'] = $this->curparams->deftitle; } if (isset($img_params['caption'])) { // replace placeholders for current image number and total image count with actual values $img_params['caption'] = str_replace(array('{$current}', '{$total}'), array((string) ($index+1), $total), $img_params['caption']); } $img_attrs['alt'] = isset($img_params['caption']) ? strip_tags($img_params['caption']) : ''; if (!isset($img_params['summary']) && $this->curparams->defdescription !== false) { // set default description if no description is supplied $img_params['summary'] = $this->curparams->defdescription; } if (isset($img_params['summary'])) { $img_params['summary'] = str_replace(array('{$current}', '{$total}'), array((string) ($index+1), $total), $img_params['summary']); } if (isset($img_params['summary'])) { // convert HTML entities in summary text and strip HTML tags, special characters in text will be escaped when added to HTML element attributes $summary = html_entity_decode(strip_tags($img_params['summary']), ENT_QUOTES, 'UTF-8'); if (isset($anchor_attrs) && !isset($anchor_attrs['title'])) { $anchor_attrs['title'] = $summary; } else { $img_attrs['title'] = $summary; } } if (SIGPLUS_DISABLE_TITLE) { unset($img_attrs['alt']); unset($anchor_attrs['title']); unset($img_params['caption']); unset($img_params['summary']); } // compose metadata field (invisible HTML div element) ob_start(); if (isset($img_params['caption']) && $img_params['caption'] != $img_attrs['alt']) { // HTML has been stripped away print '<div id="'.$imageid.'_caption">'.$img_params['caption'].'</div>'; } // summary text to display below image if (isset($img_params['summary'])) { print '<div id="'.$imageid.'_summary">'.$img_params['summary'].'</div>'; } // image icons if (isset($img_params['fullsize'])) { print '<a rel="download" href="'.$img_params['fullsize'].'"></a>'; } // image metadata if (isset($imagedata['metadata'])) { // display IPTC image metadata in pop-up window if set print '<div id="'.$imageid.'_iptc">'; print '<table>'; foreach ($imagedata['metadata'] as $key => $value) { print '<tr><th>'.htmlspecialchars($key).'</th>'; if (is_array($value)) { $stringvalue = implode(', ', $value); } else { $stringvalue = $value; } print '<td>'.nl2br(htmlspecialchars($stringvalue)).'</td></tr>'; } print '</table>'; print '</div>'; } $meta = ob_get_clean(); if ($this->curparams->maxcount > 0 && $index >= $this->curparams->maxcount) { // images in excess of maximum thumbnail count print '<li style="display:none !important;">'; // images are shown in pop-up window but not on page } else { print '<li>'; } $imagehtml = make_html('img', $img_attrs); if (isset($anchor_attrs)) { print make_html('a', $anchor_attrs, $imagehtml); } else { print $imagehtml; } if ($meta) { print '<div id="'.$imageid.'_metadata" style="display:none !important;">'.$meta.'</div>'; } print '</li>'; return ob_get_clean(); } /** * Adds style and script declarations for an image gallery. */ private function addStylesAndScripts($galleryid) { // add styles and scripts for image gallery $engineservices = SIGPlusEngineServices::instance(); $engineservices->addStyleDefaultLanguage(); $engineservices->addStyles(); $lightbox = $engineservices->getLightboxEngine($this->curparams->lightbox); // get selected lightbox engine if any, or use default if ($lightbox) { $lightbox->addStyles(); $lightbox->addScripts($galleryid, $this->curparams); } if ($this->curparams->metadata) { $metadatabox = $engineservices->getMetadataEngine($this->curparams->lightbox); if ($metadatabox) { $metadatabox->addStyles(); $metadatabox->addMetadataScripts(); } } $slider = $engineservices->getSliderEngine($this->curparams->slider); // get selected slider engine if any, or use default if ($slider) { // use image thumbnail navigation controls $slider->addStyles(); $slider->addScripts($galleryid, $this->curparams); } $captions = $engineservices->getCaptionsEngine($this->curparams->captions); // get selected captions engine if any, or use default if ($captions) { if ($this->curparams->metadata) { $captions->showMetadata(true); } if ($this->curparams->download) { $captions->showDownload(true); } $captions->addStyles(); $captions->addScripts($galleryid, $this->curparams); } // add custom style declaration based on back-end and inline settings $cssrules = array(); if ($this->curparams->margin !== false) { $cssrules[] = 'margin:'.$this->curparams->margin.' !important;'; } if ($this->curparams->borderwidth !== false && $this->curparams->borderstyle !== false && $this->curparams->bordercolor !== false) { $cssrules[] = 'border:'.$this->curparams->borderwidth.' '.$this->curparams->borderstyle.' #'.$this->curparams->bordercolor.' !important;'; } else { if ($this->curparams->borderwidth !== false) { $cssrules[] = 'border-width:'.$this->curparams->borderwidth.' !important;'; } if ($this->curparams->borderstyle !== false) { $cssrules[] = 'border-style:'.$this->curparams->borderstyle.' !important;'; } if ($this->curparams->bordercolor !== false) { $cssrules[] = 'border-color:#'.$this->curparams->bordercolor.' !important;'; } } if ($this->curparams->padding !== false) { $cssrules[] = 'padding:'.$this->curparams->padding.' !important;'; } if (!empty($cssrules)) { $document = JFactory::getDocument(); $selectors = array( 'ul > li img'); if ($slider && ($selector = $slider->getImageStyleSelector())) { $selectors[] = $selector; } foreach ($selectors as &$selector) { $selector = '#'.$galleryid.' '.$selector; } $document->addStyleDeclaration(implode(', ', $selectors).' { '.implode("\n", $cssrules).' }'); } //$document->addStyleDeclaration('#'.$galleryid.' ul > li { width: '.$this->curparams->width.'px; height: '.$this->curparams->height.'px; }'); $engineservices->addOnReadyScripts(); } /** * Generates an image gallery whose images come from Picasa Web Albums. * @see http://picasaweb.google.com * @param string $url The Picasa album RSS feed URL. * @param string $galleryid An identifier for the gallery to generate. */ private function getPicasaImageGallery($url, $galleryid) { // check for presence of XML parser if (!function_exists('simplexml_load_file')) { throw new SIGPlusNotSupportedException(); } // parse album feed URL $urlparts = parse_url(htmlspecialchars_decode($url)); // extract Picasa user identifier and album identifier from feed URL $urlpath = $urlparts['path']; $match = array(); if (!preg_match('"^/data/feed/(?:api|base)/user/([^/?#]+)/albumid/([^/?#]+)"', $urlpath, $match)) { return array(); } $userid = $match[1]; $albumid = $match[2]; // extract feed URL parameters (including authorization key if any) $urlquery = array(); if (isset($urlparts['query'])) { parse_str($urlparts['query'], $urlquery); } // define fixed thumbnail sizes provided by Picasa $sizes_cropped = array(32, 48, 64, 72, 104, 144, 150, 160); $sizes_uncropped = array_merge($sizes_cropped, array(94, 110, 128, 200, 220, 288, 320, 400, 512, 576, 640, 720, 800, 912, 1024, 1152, 1280, 1440, 1600)); sort($sizes_uncropped); // choose cropped vs. uncropped if ($this->curparams->crop) { $sizes = $sizes_cropped; $crop = 'c'; } else { $sizes = $sizes_uncropped; $crop = 'u'; } // get thumbnail size(s) that best match(es) expected preview image dimensions $mindim = min($this->curparams->width, $this->curparams->height); // smaller dimension $minsize = $sizes[0]; for ($k = 0; $k < count($sizes) && $mindim >= $sizes[$k]; $k++) { // smaller than both width and height $minsize = $sizes[$k]; } $preferred = array($minsize); $maxdim = max($this->curparams->width, $this->curparams->height); // larger dimension for ($k = 0; $k < count($sizes) && $maxdim >= $sizes[$k]; $k++) { $preferred[] = $sizes[$k]; } sort($preferred, SORT_REGULAR); $preferred = array_unique($preferred, SORT_REGULAR); // build URL query string to fetch list of photos in album $feedquery = array( 'kind' => 'photo', 'thumbsize' => implode($crop.',', $preferred).$crop, // preferred thumb sizes 'fields' => 'id,updated,entry(id,updated,media:group)' // fetch only the listed XML elements ); if ($this->curparams->maxcount) { $feedquery['max-results'] = $this->curparams->maxcount; } if (isset($urlquery['authkey'])) { // pass on authorization key $feedquery['authkey'] = $urlquery['authkey']; } // build URL to fetch list of photos in album $uri = JFactory::getURI(); $scheme = $uri->isSSL() ? 'https:' : 'http:'; $feedurl = $scheme.'//picasaweb.google.com/data/feed/api/user/'.$userid.'/albumid/'.$albumid.'?'.http_build_query($feedquery); // get XML file of list of photos in an album $sxml = simplexml_load_file($feedurl); if ($sxml === false) { return array(); } $items = array(); $index = 0; foreach ($sxml->entry as $entry) { // enumerate album entries with XPath "/feed/entry" $media = $entry->children('http://search.yahoo.com/mrss/'); // children with namespace "media" $mediagroup = $media->group; // get image title and description $title = (string) $mediagroup->title; $description = (string) $mediagroup->description; // get image URL $attrs = $mediagroup->content->attributes(); $imageurl = (string) $attrs['url']; // <media:content url='...' height='...' width='...' type='image/jpeg' medium='image' /> // get preview image URL $thumburl = null; $thumbwidth = 0; $thumbheight = 0; foreach ($mediagroup->thumbnail as $thumbnail) { $attrs = $thumbnail->attributes(); $curwidth = (int) $attrs['width']; $curheight = (int) $attrs['height']; // update thumbnail to use if it fits in image bounds if ($this->curparams->width >= $curwidth && $this->curparams->height >= $curheight && ($curwidth > $thumbwidth || $curheight > $thumbheight)) { $thumburl = (string) $attrs['url']; // <media:thumbnail url='...' height='...' width='...' /> $thumbwidth = $curwidth; $thumbheight = $curheight; } } // build image data $imagedata = array( 'image' => array( 'width' => $thumbwidth, 'height' => $thumbheight, 'preview' => $thumburl ), 'anchor' => array( 'href' => $imageurl ) ); if ($title) { $imagedata['image']['caption'] = $title; } if ($description) { $imagedata['image']['summary'] = $description; } // generate code switch ($this->curparams->linkage) { case 'inline': $items[] = $this->getPreviewHtml($galleryid, $index, count($sxml->entry), $imagedata); break; default: $items[] = $this->getPreviewScript($galleryid, $index, count($sxml->entry), $imagedata); } $index++; } return $items; } /** * Generates an image gallery entirely defined with a list of label objects of remote URLs. * @param $list An array of label objects. */ private function getUserDefinedRemoteImageGallery(array $list, $galleryid) { foreach ($list as $listitem) { $this->createPreviewImage($listitem->imagefile); } $images = array(); foreach ($list as $listitem) { if (($image = $this->getImageData($listitem->imagefile, count($images), $listitem)) === false) { continue; } $images[] = $image; // add image to list of image data } $items = array(); foreach ($images as $index => $image) { switch ($this->curparams->linkage) { case 'inline': $items[] = $this->getPreviewHtml($galleryid, $index, count($images), $image); break; default: $items[] = $this->getPreviewScript($galleryid, $index, count($images), $image); } } return $items; } /** * Generates an image gallery entirely defined with a list of filenames or a list of label objects. * @param $imagedirectory An absolute path to a directory. * @param $list An array of filenames and/or label objects. */ private function getUserDefinedImageGallery($imagedirectory, array $list, $galleryid) { foreach ($list as $listitem) { if (is_string($listitem)) { $this->createPreviewImage($imagedirectory.DIRECTORY_SEPARATOR.$listitem); } else { $this->createPreviewImage($imagedirectory.DIRECTORY_SEPARATOR.$listitem->imagefile); } } $images = array(); foreach ($list as $listitem) { if (is_string($listitem)) { $image = $this->getImageData($imagedirectory.DIRECTORY_SEPARATOR.$listitem, count($images)); } else { $image = $this->getImageData($imagedirectory.DIRECTORY_SEPARATOR.$listitem->imagefile, count($images), $listitem); } if ($image === false) { continue; } $images[] = $image; // add image to list of image data } $items = array(); foreach ($images as $index => $image) { switch ($this->curparams->linkage) { case 'inline': $items[] = $this->getPreviewHtml($galleryid, $index, count($images), $image); break; default: $items[] = $this->getPreviewScript($galleryid, $index, count($images), $image); } } return $items; } /** * Generates an image gallery where some files have labels. * @param $imagedirectory An absolute path to a directory in the file system. */ private function getLabeledImageGallery($imagedirectory, $files, $labels, $galleryid) { if (empty($files)) { return false; } $labelmap = array(); foreach ($labels as $label) { // enumerate images listed in labels.txt $labelmap[$label->imagefile] = $label; } $files = array_filter($files, 'is_imagefile'); foreach ($files as $file) { $this->createPreviewImage($imagedirectory.DIRECTORY_SEPARATOR.$file); } $images = array(); foreach ($files as $file) { if (($image = $this->getImageData($imagedirectory.DIRECTORY_SEPARATOR.$file, count($images), array_value($labelmap, $file))) === false) { continue; } $images[] = $image; } $items = array(); foreach ($images as $index => $image) { switch ($this->curparams->linkage) { case 'inline': $items[] = $this->getPreviewHtml($galleryid, $index, count($images), $image); break; default: $items[] = $this->getPreviewScript($galleryid, $index, count($images), $image); } } return $items; } /** * Generates an image gallery where files have no labels. * @param $imagedirectory An absolute path to a directory in the file system. */ private function getUnlabeledImageGallery($imagedirectory, $files, $galleryid) { return $this->getLabeledImageGallery($imagedirectory, $files, array(), $galleryid); } /** * Ensures that a gallery identifier is unique across the page. * A gallery identifier is specified by the user or generated from the relative image path. Other extensions, * however, may duplicate article content on the page (e.g. show a short article extract in a module position), * making an identifier no longer unique. This function adds an ordinal to prevent conflicts when the same gallery * would occur multiple times on the page, causing scripts not to function properly. */ private function getUniqueGalleryId($galleryid) { if (in_array($galleryid, self::$galleryids)) { // look for identifier in script-lifetime container $counter = 1000; do { $counter++; $gid = $galleryid.'_'.$counter; } while (in_array($gid, self::$galleryids)); $galleryid = $gid; } self::$galleryids[] = $galleryid; return $galleryid; } /** * Generates image previews with alternate text, title and lightbox pop-up activation on mouse click. * @param string $body Data associated with the gallery. * @param string $paramstring A whitespace-separated list of name="value" parameter values. */ private function getImageGalleryHtml($body, $params = array()) { // set gallery parameters $this->curparams = clone $this->defparams; // parameters set in back-end if (is_array($params)) { $this->curparams->setArray($params); } else { $paramstring = htmlspecialchars_decode((string) $params); $this->curparams->setString($paramstring); // parameters set inline } if (!isset($body)) { // path is set via parameter with compact activation syntax $body = $this->curparams->path; } $engineservices = SIGPlusEngineServices::instance(); // generate link to an existing gallery if ($this->curparams->link !== false) { $lightbox = $engineservices->getLightboxEngine($this->curparams->lightbox); // get selected lightbox engine if any or use default if ($lightbox && ($linkscript = $lightbox->getLinkScript($this->curparams->link, $this->curparams->index)) !== false) { return '<a href="javascript:void('.htmlspecialchars($linkscript).')">'.$body.'</a>'; } else { // engine does not support programmatic activation return $body; } } // set gallery folders $imageref = $body; // a relative path to an image folder or an image, or an absolute URL to an image to display if ($isremote = is_remote_path($imageref)) { $imageurl = $imageref; $iswebalbum = (bool) preg_match('"^https?://picasaweb.google.com/data/feed/(?:api|base)/user/([^/?#]+)/albumid/([^/?#]+)"', $imageurl); // test for Picasa galleries $imagehashbase = $imageurl; } else { $imageref = trim($imageref, '/'); // remove leading and trailing backslash // verify validity of relative path $imagepath = $this->imageservices->getImagePath($imageref); if (!file_exists($imagepath)) { throw new SIGPlusImageGalleryFolderException($imageref); } $imagehashbase = $imagepath; // base in computing hash for content caching } // set gallery identifier if ($this->curparams->id) { // use user-supplied identifier $galleryid = $this->curparams->id; } else { // automatically generate identifier for thumbnail gallery $galleryid = 'sigplus_'.md5($imagehashbase); } $galleryid = $this->getUniqueGalleryId($galleryid); // force meaningful settings for single-image view (disable slider and activate flow layout) if ($this->curparams->layout != 'hidden' && ($isremote && !$iswebalbum || isset($imagepath) && is_file($imagepath))) { $this->curparams->layout = 'flow'; $this->curparams->rows = false; $this->curparams->cols = false; $this->curparams->slider = false; } // substitute proper left or right alignment depending on whether language is LTR or RTL $language = JFactory::getLanguage(); $this->curparams->alignment = str_replace(array('after','before'), $language->isRTL() ? array('left','right') : array('right','left'), $this->curparams->alignment); // get selected slider engine if any, or use default $slider = $engineservices->getSliderEngine($this->curparams->slider); if (!$slider) { $this->curparams->progressive = false; // progressive loading is not supported unless a slider is enabled } // *** cannot update $this->curparams, which is used in content caching, beyond this point *** // // initialize logging if (SIGPLUS_LOGGING) { $logging = SIGPlusLogging::instance(); if ($isremote) { $logging->append('Generating gallery "'.$galleryid.'" from URL: <kbd>'.$imageurl.'</kbd>'); } else { $logging->append('Generating gallery "'.$galleryid.'" from file/directory: <kbd>'.$imagepath.'</kbd>'); } $logging->appendblock('Local parameters for "'.$galleryid.'" are:', print_r($this->curparams, true)); } // verify if content is available in cache folder if (!SIGPLUS_CONTENT_CACHING || $engineservices->debug || $this->curparams->hasRandom()) { $cachekey = false; // galleries that involve a random element cannot be cached } elseif (($cachekey = $this->imageservices->getCachedContent($imagehashbase, $this->curparams)) !== false) { if (SIGPLUS_LOGGING) { $logging->append('Retrieving cached content with key <kbd>'.$cachekey.'</kbd>.'); } } // generate gallery HTML code or setup script if ($cachekey === false) { // save default title and description, which might be overridden in labels file, affecting hash key used in caching $deftitle = $this->curparams->deftitle; $defdescription = $this->curparams->defdescription; if ($isremote) { // access images remote domain if ($iswebalbum) { $htmlorscript = $this->getPicasaImageGallery($imageurl, $galleryid); } else { $extension = strtolower(pathinfo(parse_url($imageurl, PHP_URL_PATH), PATHINFO_EXTENSION)); switch ($extension) { case 'gif': case 'jpg': case 'jpeg': case 'png': // plug-in syntax {gallery}http://example.com/image.jpg{/gallery} $labels = array(new SIGPlusImageLabel($imageurl, false, false)); // artificial single-entry labels file $htmlorscript = $this->getUserDefinedRemoteImageGallery($labels, $galleryid); break; default: // plug-in syntax {gallery}http://example.com{/gallery} throw new SIGPlusNotSupportedException(); $labels = $this->imageservices->getLabels($imageurl, $this->curparams->labels, $this->curparams->deftitle, $this->curparams->defdescription); switch ($this->curparams->sortcriterion) { case SIGPLUS_SORT_RANDOMLABELS: shuffle($labels); // fall through case SIGPLUS_SORT_LABELS_OR_FILENAME: case SIGPLUS_SORT_LABELS_OR_MTIME: $htmlorscript = $this->getUserDefinedRemoteImageGallery($labels, $galleryid); } } } } else { if (is_file($imagepath)) { // syntax {gallery}folder/subfolder/file.jpg{/gallery} $htmlorscript = $this->getUnlabeledImageGallery(dirname($imagepath), array(basename($imagepath)), $galleryid); } else { // syntax {gallery}folder/subfolder{/gallery} // fetch image labels switch ($this->curparams->labels) { case 'filename': $labels = $this->imageservices->getLabelsFromFilenames($imagepath); break; default: $labels = $this->imageservices->getLabels($imagepath, $this->curparams->labels, $this->curparams->deftitle, $this->curparams->defdescription); } switch ($this->curparams->sortcriterion) { case SIGPLUS_SORT_LABELS_OR_FILENAME: if (empty($labels)) { // there is no labels file to use $files = $this->imageservices->getListing($imagepath, SIGPLUS_FILENAME, $this->curparams->sortorder, $this->curparams->depth); $htmlorscript = $this->getUnlabeledImageGallery($imagepath, $files, $galleryid); } else { $htmlorscript = $this->getUserDefinedImageGallery($imagepath, $labels, $galleryid); } break; case SIGPLUS_SORT_LABELS_OR_MTIME: if (empty($labels)) { $files = $this->imageservices->getListing($imagepath, SIGPLUS_MTIME, $this->curparams->sortorder, $this->curparams->depth); $htmlorscript = $this->getUnlabeledImageGallery($imagepath, $files, $galleryid); } else { $htmlorscript = $this->getUserDefinedImageGallery($imagepath, $labels, $galleryid); } break; case SIGPLUS_SORT_MTIME: $files = $this->imageservices->getListing($imagepath, SIGPLUS_MTIME, $this->curparams->sortorder, $this->curparams->depth); $htmlorscript = $this->getLabeledImageGallery($imagepath, $files, $labels, $galleryid); break; case SIGPLUS_SORT_RANDOM: $files = $this->imageservices->getListing($imagepath, SIGPLUS_RANDOM, $this->curparams->sortorder, $this->curparams->depth); $htmlorscript = $this->getLabeledImageGallery($imagepath, $files, $labels, $galleryid); break; case SIGPLUS_SORT_RANDOMLABELS: if (empty($labels)) { // there is no labels file to use $files = $this->imageservices->getListing($imagepath, SIGPLUS_RANDOM, $this->curparams->sortorder, $this->curparams->depth); $htmlorscript = $this->getUnlabeledImageGallery($imagepath, $files, $galleryid); } else { shuffle($labels); $htmlorscript = $this->getUserDefinedImageGallery($imagepath, $labels, $galleryid); } break; default: // case SIGPLUS_SORT_FILENAME: $files = $this->imageservices->getListing($imagepath, SIGPLUS_FILENAME, $this->curparams->sortorder, $this->curparams->depth); $htmlorscript = $this->getLabeledImageGallery($imagepath, $files, $labels, $galleryid); break; } } } if (!empty($htmlorscript)) { switch ($this->curparams->linkage) { case 'inline': $cachedata = ($slider !== false ? '<ul style="visibility:hidden;">' : '<ul>').implode($htmlorscript).'</ul>'; break; case 'head': // put generated content in HTML head (does not allow HTML body with bloating size, which would cause preg_replace in System - SEF to fail) $cachedata = $this->getGalleryScript($galleryid, $htmlorscript); break; case 'external': $cachedata = '__jQuery__(function () { '.$this->getGalleryScript($galleryid, $htmlorscript).' });'; break; } } else { $cachedata = false; } // restore default title and description, which might have been overridden in labels file $this->curparams->deftitle = $deftitle; $this->curparams->defdescription = $defdescription; if (SIGPLUS_CONTENT_CACHING && !$this->curparams->hasRandom()) { // save generated content for future re-use in a temporary file in the cache folder $this->imageservices->cleanCachedContent(); $cachekey = $this->imageservices->saveCachedContent($imagehashbase, $this->curparams, $cachedata); if (SIGPLUS_LOGGING) { if ($cachekey !== false) { $logging->append('Saved cached content with key <kbd>'.$cachekey.'</kbd>.'); } else { $logging->append('Failed to persist content in cache folder.'); } } } } elseif ($this->curparams->linkage != 'external') { // retrieve content from cache but no need to fetch content for linking external .js file $cachefile = $this->imageservices->getCachedContentPath($cachekey, $this->curparams->linkage == 'inline' ? '.html' : '.js'); if (filesize($cachefile) > 0) { $cachedata = file_get_contents($cachefile); } else { $cachedata = false; // empty gallery } } else { $cachedata = true; } if ($cachedata === false) { // no content $html = JText::_('SIGPLUS_EMPTY'); } else { switch ($this->curparams->linkage) { case 'inline': $html = $cachedata; // content produced as HTML only in inline linkage mode break; case 'head': $this->addGalleryScript(); // add gallery population script $engineservices->addOnReadyScript($cachedata); // add gallery data $html = ''; // no content produced in HTML except for placeholder break; case 'external': $this->addGalleryScript(); if ($cachekey !== false) { // include reference to generated script in external .js file $document = JFactory::getDocument(); $document->addScript($this->imageservices->getCachedContentUrl($cachekey, '.js')); } else { // add script to document head as a fall-back if could not save to external .js file in cache folder $engineservices->addOnReadyScript($cachedata); } $html = ''; break; } } // set image gallery alignment (left, center or right) and style $gallerystyle = 'sigplus-gallery'; switch ($this->curparams->alignment) { case 'left': case 'left-clear': case 'left-float': $gallerystyle .= ' sigplus-left'; break; case 'center': $gallerystyle .= ' sigplus-center'; break; case 'right': case 'right-clear': case 'right-float': $gallerystyle .= ' sigplus-right'; break; } switch ($this->curparams->alignment) { case 'left': case 'left-float': case 'right': case 'right-float': $gallerystyle .= ' sigplus-float'; break; case 'left-clear': case 'right-clear': $gallerystyle .= ' sigplus-clear'; break; } switch ($this->curparams->imagecaptions) { case 'above': $gallerystyle .= ' sigplus-captionsabove'; break; case 'below': $gallerystyle .= ' sigplus-captionsbelow'; break; } // output image gallery or gallery placeholder $div_attrs = array( 'id' => $galleryid, 'class' => $gallerystyle); if ($this->curparams->layout == 'hidden') { $div_attrs['style'] = 'display:none !important;'; } $html = make_html('div', $div_attrs, $html); // add style and script declarations $this->addStylesAndScripts($galleryid); $this->curparams = false; return $html; } /** * Adds JavaScript code that dynamically creating an image gallery from a data array. */ private function addGalleryScript() { $engineservices = SIGPlusEngineServices::instance(); $engineservices->addJQuery(); $document = JFactory::getDocument(); $document->addScript(JURI::base(true).'/plugins/content/sigplus/js/linkage'.($engineservices->debug ? '.js' : '.min.js')); } private function getGalleryScript($galleryid, $script) { $engineservices = SIGPlusEngineServices::instance(); $lightbox = $engineservices->getLightboxEngine($this->curparams->lightbox); // get selected lightbox engine if any or use default return '__jQuery__("#'.$galleryid.'").sigplusLinkage('. '['.implode(',', $script).'],'. '"'.($lightbox ? $lightbox->getLinkAttribute($galleryid) : '').'",'. // rel attribute to hook lightbox $this->curparams->maxcount.','. ($this->curparams->progressive ? 'true' : 'false').','. '"'.addslashes($this->curparams->deftitle).'",'. '"'.addslashes($this->curparams->defdescription).'");'; } /** * Generates image thumbnails with alternate text, title and lightbox pop-up activation on mouse click. * This method is typically called by the class plgContentSIGPlus, which represents the sigplus Joomla plug-in. * The method takes two parameters: * [*] A string that defines the gallery source. Relative paths are interpreted w.r.t. the image base folder, * which is passed in a configuration object to the class constructor. * If used as a plug-in, this string would normally appear enclosed in an activation tag. * [*] Gallery parameters, which are an array of parameters or a whitespace-separated list of name="value" parameter * values. * If used as a plug-in, these would normally appear as the attribute list of the activation start tag. * * If you use the plug-in, the activation code {gallery key=value}myfolder{/gallery} would translate into PHP code: * $core = new SIGPlusCore(); * $source = 'myfolder'; * $params = 'key=value'; * $core->getGalleryHtml($source, $params); * * @param string $body A string that defines the gallery source. * @param string $params An array of parameters or a whitespace-separated list of name="value" parameter values. */ public function getGalleryHtml($source, $params = array()) { if (!isset($this->imageservices)) { // global error, image services are not available throw new SIGPlusInitializationException(); } $oblevel = ob_get_level(); try { return $this->getImageGalleryHtml($source, $params); } catch (Exception $e) { // local error for ($k = ob_get_level(); $k > $oblevel; $k--) { // release output buffers ob_end_clean(); } throw $e; // re-throw exception } } /** * Adds activation code to a (fully customized) gallery. */ public function addGalleryEngines($customized = false) { if (!isset($this->imageservices)) { // global error, image services not available throw new SIGPlusInitializationException(); } $engineservices = SIGPlusEngineServices::instance(); if ($customized) { // hook anchors with image extensions to lightbox engine if any $engineservices->addStyles(); $lightbox = $engineservices->getLightboxEngine($this->defparams->lightbox); // get selected lightbox engine if any, or use default if ($lightbox) { $lightbox->addStyles(); $lightbox->addActivationScripts(); } } $engineservices->addOnReadyEvent(); } }Private